/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009 Geomatys
*
* 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.geotoolkit.gui.swing.propertyedit.styleproperty;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import org.geotoolkit.display2d.service.DefaultGlyphService;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.gui.swing.propertyedit.AbstractPropertyPane;
import org.geotoolkit.gui.swing.propertyedit.JPropertyPane;
import org.geotoolkit.font.FontAwesomeIcons;
import org.geotoolkit.font.IconBuilder;
import org.geotoolkit.gui.swing.resource.IconBundle;
import org.geotoolkit.gui.swing.resource.MessageBundle;
import org.geotoolkit.map.FeatureMapLayer;
import org.geotoolkit.style.FeatureTypeStyleListener;
import org.geotoolkit.style.MutableFeatureTypeStyle;
import org.geotoolkit.style.MutableRule;
import org.geotoolkit.style.MutableStyleFactory;
import org.geotoolkit.style.category.CategoryStyleBuilder;
import org.geotoolkit.style.interval.DefaultRandomPalette;
import org.geotoolkit.style.interval.RandomPalette;
import org.geotoolkit.style.visitor.CopyStyleVisitor;
import org.geotoolkit.util.collection.CollectionChangeEvent;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.combobox.ListComboBoxModel;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.HighlighterFactory;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import org.opengis.style.Rule;
import org.opengis.style.SemanticType;
import org.opengis.style.Symbolizer;
import org.opengis.util.GenericName;
/**
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class JClassificationSingleStylePanel extends AbstractPropertyPane{
private static final ImageIcon ICON_DELETE = IconBuilder.createIcon(FontAwesomeIcons.ICON_TRASH_O, 16, FontAwesomeIcons.DEFAULT_COLOR);
private final Dimension GLYPH_DIMENSION = new Dimension(30, 20);
private static final List<RandomPalette> PALETTES;
static{
PALETTES = new ArrayList<RandomPalette>();
PALETTES.add(new DefaultRandomPalette());
}
private static final MutableStyleFactory SF = (MutableStyleFactory) FactoryFinder.getStyleFactory(new Hints(Hints.STYLE_FACTORY, MutableStyleFactory.class));
private static final FilterFactory FF = FactoryFinder.getFilterFactory(null);
private final CategoryStyleBuilder builder = new CategoryStyleBuilder();
private final RuleModel model = new RuleModel();
private FeatureMapLayer layer = null;
public JClassificationSingleStylePanel() {
super(MessageBundle.format("property_style_classification_unique"),
IconBundle.getIcon("16_classification_single"),
IconBundle.getIcon("preview_style_class1").getImage(),
"");
initComponents();
guiTable.setModel(model);
guiProperty.setRenderer(new PropertyRenderer());
guiPalette.setModel(new ListComboBoxModel(PALETTES));
guiPalette.setRenderer(new PaletteRenderer());
guiPalette.setSelectedIndex(0);
builder.setPalette((RandomPalette) guiPalette.getSelectedItem());
guiTable.getColumnModel().getColumn(0).setCellRenderer(new RuleStyleRenderer());
guiTable.getColumnModel().getColumn(0).setCellEditor(new RuleStyleEditor());
guiTable.getColumnModel().getColumn(1).setCellRenderer(new RulePropertyRenderer());
guiTable.getColumnModel().getColumn(1).setCellEditor(new RulePropertyEditor());
guiTable.getColumnModel().getColumn(2).setCellRenderer(new RuleNameRenderer());
guiTable.getColumnModel().getColumn(2).setCellEditor(new RuleNameEditor());
guiTable.getColumnModel().getColumn(3).setCellRenderer(new DeleteRenderer());
guiTable.getColumnModel().getColumn(3).setCellEditor(new DeleteEditor());
guiTable.setShowGrid(false, false);
guiTable.setHighlighters(new Highlighter[]{HighlighterFactory.createAlternateStriping(Color.WHITE, HighlighterFactory.QUICKSILVER, 1)});
guiTable.getColumnExt(0).setMaxWidth(30);
guiTable.getColumnExt(3).setMaxWidth(20);
}
private void parse(){
builder.analyze(layer);
guiOther.setSelected(false);
guiProperty.setModel(new ListComboBoxModel(builder.getProperties()));
guiProperty.setSelectedItem(builder.getCurrentProperty());
updateModelGlyph();
}
private void updateModelGlyph(){
if(builder.getTemplate() == null){
guiModel.setIcon(null);
guiModel.setText(" ");
}else{
BufferedImage img = new BufferedImage(30, 20, BufferedImage.TYPE_INT_ARGB);
DefaultGlyphService.render(builder.getTemplate(), new Rectangle(GLYPH_DIMENSION), img.createGraphics(),null);
guiModel.setIcon(new ImageIcon(img));
guiModel.setText("");
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
guiAddOne = new JButton();
guiRemoveAll = new JButton();
jPanel1 = new JPanel();
guiLblProperty = new JLabel();
guiOther = new JCheckBox();
guiProperty = new JComboBox();
guiLblPalette = new JLabel();
guiLblModel = new JLabel();
guiModel = new JButton();
guiGenerate = new JButton();
guiPalette = new JComboBox();
jScrollPane1 = new JScrollPane();
guiTable = new JXTable();
guiAddOne.setText(MessageBundle.format("add_value")); // NOI18N
guiAddOne.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiAddOneActionPerformed(evt);
}
});
guiRemoveAll.setText(MessageBundle.format("remove_all_values")); // NOI18N
guiRemoveAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiRemoveAllActionPerformed(evt);
}
});
jPanel1.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 5));
guiLblProperty.setText(MessageBundle.format("property")); // NOI18N
guiOther.setText(MessageBundle.format("otherRule")); // NOI18N
guiLblPalette.setText(MessageBundle.format("palette")); // NOI18N
guiLblModel.setText(MessageBundle.format("model")); // NOI18N
guiModel.setText(" ");
guiModel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiModelActionPerformed(evt);
}
});
guiGenerate.setText(MessageBundle.format("generate")); // NOI18N
guiGenerate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
guiGenerateActionPerformed(evt);
}
});
GroupLayout jPanel1Layout = new GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(guiLblPalette)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiPalette, 0, 309, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiLblModel)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiModel))
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(guiLblProperty)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiProperty, 0, 288, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiOther)))
.addContainerGap())
.addComponent(guiGenerate, Alignment.TRAILING)))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiLblProperty)
.addComponent(guiProperty, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(guiOther))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiLblPalette, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
.addComponent(guiPalette, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(guiLblModel, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
.addComponent(guiModel))
.addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(guiGenerate))
);
jPanel1Layout.linkSize(SwingConstants.VERTICAL, new Component[] {guiLblModel, guiLblPalette, guiModel});
jScrollPane1.setViewportView(guiTable);
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addComponent(jPanel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 485, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(guiAddOne)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(guiRemoveAll)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jPanel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 205, Short.MAX_VALUE)
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(Alignment.BASELINE)
.addComponent(guiAddOne)
.addComponent(guiRemoveAll)))
);
}// </editor-fold>//GEN-END:initComponents
private void guiAddOneActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiAddOneActionPerformed
String val = JOptionPane.showInputDialog(MessageBundle.format("value")+" :");
MutableRule r = builder.createRule((PropertyName) guiProperty.getSelectedItem(), val);
model.fts.rules().add(r);
}//GEN-LAST:event_guiAddOneActionPerformed
private void guiRemoveAllActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiRemoveAllActionPerformed
model.fts.rules().clear();
}//GEN-LAST:event_guiRemoveAllActionPerformed
private void guiGenerateActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiGenerateActionPerformed
builder.setPalette((RandomPalette) guiPalette.getSelectedItem());
builder.setOther(guiOther.isSelected());
builder.setCurrentProperty((PropertyName) guiProperty.getSelectedItem());
builder.create();
}//GEN-LAST:event_guiGenerateActionPerformed
private void guiModelActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiModelActionPerformed
builder.setTemplate( JPropertyPane.showSymbolizerDialog(this, builder.getTemplate(), true, layer) );
updateModelGlyph();
}//GEN-LAST:event_guiModelActionPerformed
@Override
public boolean canHandle(Object target) {
return target instanceof FeatureMapLayer;
}
@Override
public void setTarget(final Object layer) {
if(layer instanceof FeatureMapLayer){
this.layer = (FeatureMapLayer) layer;
parse();
}else{
this.layer = null;
}
}
@Override
public void apply() {
if(layer == null) return;
layer.getStyle().featureTypeStyles().clear();
MutableFeatureTypeStyle fts = SF.featureTypeStyle(
"", SF.description("analyze", "analyze"), null, null, null, (List)model.fts.rules());
layer.getStyle().featureTypeStyles().add( CopyStyleVisitor.INSTANCE.visit(fts,null) );
}
@Override
public void reset() {
if(layer != null){
parse();
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private JButton guiAddOne;
private JButton guiGenerate;
private JLabel guiLblModel;
private JLabel guiLblPalette;
private JLabel guiLblProperty;
private JButton guiModel;
private JCheckBox guiOther;
private JComboBox guiPalette;
private JComboBox guiProperty;
private JButton guiRemoveAll;
private JXTable guiTable;
private JPanel jPanel1;
private JScrollPane jScrollPane1;
// End of variables declaration//GEN-END:variables
private class PropertyRenderer extends DefaultListCellRenderer{
@Override
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value instanceof PropertyName){
setText(((PropertyName)value).getPropertyName());
}
return PropertyRenderer.this;
}
}
private class PaletteRenderer extends DefaultListCellRenderer{
private RandomPalette palette = null;
@Override
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
PaletteRenderer.this.setText(" Random ");
if(value instanceof RandomPalette){
palette = (RandomPalette)value;
}
return PaletteRenderer.this;
}
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
if(palette != null){
Dimension d = PaletteRenderer.this.getSize();
Rectangle rect = new Rectangle(d);
rect.grow(-2, -2);
palette.render((Graphics2D) g, rect,false);
}
}
}
private class DeleteRenderer extends DefaultTableCellRenderer{
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
DeleteRenderer.this.setIcon(ICON_DELETE);
return DeleteRenderer.this;
}
}
private class DeleteEditor extends AbstractCellEditor implements TableCellEditor{
private final JButton button = new JButton();
private Object value;
public DeleteEditor() {
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setIcon(ICON_DELETE);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(value != null && value instanceof Rule){
model.fts.rules().remove(value);
}
}
});
}
@Override
public Object getCellEditorValue() {
return null;
}
@Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
this.value = value;
return button;
}
}
private class RulePropertyRenderer extends DefaultTableCellRenderer{
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
RulePropertyRenderer.this.setText("");
if(value instanceof Rule){
Rule r = (Rule) value;
Filter f = r.getFilter();
if(f != null && f instanceof PropertyIsEqualTo){
PropertyIsEqualTo prop = (PropertyIsEqualTo) f;
if(prop.getExpression1() instanceof Literal){
RulePropertyRenderer.this.setText(prop.getExpression1().toString());
}else if(prop.getExpression2() instanceof Literal){
RulePropertyRenderer.this.setText(prop.getExpression2().toString());
}
}
}
return RulePropertyRenderer.this;
}
}
private class RulePropertyEditor extends AbstractCellEditor implements TableCellEditor{
private final JTextField field = new JTextField();
private MutableRule value;
public RulePropertyEditor() {
}
@Override
public Object getCellEditorValue() {
return FF.literal(field.getText());
}
@Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
if(value instanceof MutableRule){
this.value = (MutableRule) value;
Filter f = this.value.getFilter();
if(f != null && f instanceof PropertyIsEqualTo){
PropertyIsEqualTo prop = (PropertyIsEqualTo) f;
if(prop.getExpression1() instanceof Literal){
field.setText(prop.getExpression1().toString());
}else if(prop.getExpression2() instanceof Literal){
field.setText(prop.getExpression2().toString());
}
}
}else{
this.value = null;
}
return field;
}
}
private class RuleNameRenderer extends DefaultTableCellRenderer{
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
RuleNameRenderer.this.setText("");
if(value instanceof Rule){
Rule r = (Rule) value;
RuleNameRenderer.this.setText(r.getDescription().getTitle().toString());
}
return RuleNameRenderer.this;
}
}
private class RuleNameEditor extends AbstractCellEditor implements TableCellEditor{
private final JTextField field = new JTextField();
private MutableRule value;
public RuleNameEditor() {
}
@Override
public Object getCellEditorValue() {
return field.getText();
}
@Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
if(value instanceof MutableRule){
this.value = (MutableRule) value;
field.setText(this.value.getDescription().getTitle().toString());
}else{
field.setText("");
this.value = null;
}
return field;
}
}
private class RuleStyleRenderer extends DefaultTableCellRenderer{
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
RuleStyleRenderer.this.setText("");
if(value instanceof Rule){
BufferedImage img = new BufferedImage(30, 20, BufferedImage.TYPE_INT_ARGB);
DefaultGlyphService.render((Rule)value, new Rectangle(GLYPH_DIMENSION), img.createGraphics(),null);
RuleStyleRenderer.this.setIcon(new ImageIcon(img));
}
return RuleStyleRenderer.this;
}
}
private class RuleStyleEditor extends AbstractCellEditor implements TableCellEditor{
private final JButton button = new JButton();
private MutableRule value;
public RuleStyleEditor() {
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(value != null){
Symbolizer symbol = JPropertyPane.showSymbolizerDialog(JClassificationSingleStylePanel.this, value.symbolizers().get(0), layer);
value.symbolizers().clear();
value.symbolizers().add(symbol);
}
}
});
}
@Override
public Object getCellEditorValue() {
return null;
}
@Override
public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
if(value instanceof MutableRule){
this.value = (MutableRule) value;
BufferedImage img = new BufferedImage(30, 20, BufferedImage.TYPE_INT_ARGB);
DefaultGlyphService.render(this.value, new Rectangle(GLYPH_DIMENSION), img.createGraphics(),null);
button.setIcon(new ImageIcon(img));
}else{
this.value = null;
}
return button;
}
}
private class RuleModel extends AbstractTableModel{
private final MutableFeatureTypeStyle fts = builder.getFeatureTypeStyle();
public RuleModel() {
fts.addListener(new FeatureTypeStyleListener() {
@Override
public void ruleChange(CollectionChangeEvent<MutableRule> event) {
RuleModel.this.fireTableDataChanged();
}
@Override
public void featureTypeNameChange(CollectionChangeEvent<GenericName> event) {}
@Override
public void semanticTypeChange(CollectionChangeEvent<SemanticType> event) {}
@Override
public void propertyChange(PropertyChangeEvent evt) {}
});
}
@Override
public int getRowCount() {
return fts.rules().size();
}
@Override
public int getColumnCount() {
return 4;
}
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
return true;
}
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
return fts.rules().get(rowIndex);
}
@Override
public String getColumnName(final int columnIndex) {
switch(columnIndex){
case 0: return "";
case 1: return MessageBundle.format("value");
case 2: return MessageBundle.format("title");
case 3: return "";
}
return "";
}
@Override
public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
MutableRule rule = (MutableRule) fts.rules().get(rowIndex);
if(aValue instanceof Literal){
//the property value field has changed
Filter f = rule.getFilter();
if(f != null){
PropertyIsEqualTo prop = (PropertyIsEqualTo) f;
if(prop.getExpression1() instanceof Literal){
rule.setFilter(FF.equals(prop.getExpression2(),(Expression) aValue));
}else if(prop.getExpression2() instanceof Literal){
rule.setFilter(FF.equals(prop.getExpression1(),(Expression) aValue));
}
}
}else if(aValue instanceof String){
//the rule title changed
rule.setDescription(SF.description((String)aValue, (String)aValue));
}
super.setValueAt(aValue, rowIndex, columnIndex);
}
}
}