/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2012 - 2014 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.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.EventObject; import java.util.List; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import org.apache.sis.util.ArgumentChecks; import org.geotoolkit.gui.swing.propertyedit.PropertyPane; 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.gui.swing.style.JBankPanel; import org.geotoolkit.gui.swing.style.JPreview; import org.geotoolkit.gui.swing.style.StyleElementEditor; import org.geotoolkit.gui.swing.util.ActionCell; import org.geotoolkit.gui.swing.util.JOptionDialog; import org.geotoolkit.map.LayerListener; import org.geotoolkit.map.MapItem; import org.geotoolkit.map.MapLayer; import org.geotoolkit.style.MutableFeatureTypeStyle; import org.geotoolkit.style.MutableRule; import org.geotoolkit.style.MutableStyle; import org.geotoolkit.style.RuleListener; import org.geotoolkit.util.collection.CollectionChangeEvent; import org.jdesktop.swingx.JXTable; import org.opengis.style.FeatureTypeStyle; import org.opengis.style.LineSymbolizer; import org.opengis.style.PointSymbolizer; import org.opengis.style.PolygonSymbolizer; import org.opengis.style.RasterSymbolizer; import org.opengis.style.Rule; import org.opengis.style.Symbolizer; import org.opengis.style.TextSymbolizer; /** * Frame of simple style editor * @author Fabien Rétif (Geomatys) * @author Johann Sorel (Geomatys) */ public class JSimpleStylePanel extends StyleElementEditor implements PropertyPane, LayerListener{ private static final ImageIcon ICO_UP = IconBuilder.createIcon(FontAwesomeIcons.ICON_CHEVRON_UP, 16, FontAwesomeIcons.DEFAULT_COLOR); private static final ImageIcon ICO_DOWN = IconBuilder.createIcon(FontAwesomeIcons.ICON_CHEVRON_DOWN, 16, FontAwesomeIcons.DEFAULT_COLOR); private static final ImageIcon ICO_ADD = IconBuilder.createIcon(FontAwesomeIcons.ICON_PLUS, 16, FontAwesomeIcons.DEFAULT_COLOR); private static final ImageIcon ICO_DELETE = IconBuilder.createIcon(FontAwesomeIcons.ICON_TRASH_O, 16, FontAwesomeIcons.DEFAULT_COLOR); private final SymbolizerModel model = new SymbolizerModel(getStyleFactory().rule()); private MapLayer layer = null; private MutableStyle style = null; private JBankPanel bankController = new JBankPanel(); private final PropertyChangeListener changeListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if(PROPERTY_UPDATED.equals(evt.getPropertyName())){ if (evt.getNewValue() instanceof Symbolizer) { final Symbolizer s = (Symbolizer) evt.getNewValue(); guiOverviewLabel.parse(s); final int selecteRow = guiTable.getSelectedRow(); if (selecteRow >= 0) { model.rule.symbolizers().set(selecteRow,s); } } } } }; private final LayerListener.Weak layerListener = new LayerListener.Weak(this); //current visible editor private StyleElementEditor currentEditor = null; //keep track of where the rule was to avoid rewriting the complete style private MutableRule parentRule = null; /** * Creates new form jStylePane */ public JSimpleStylePanel() { super(Object.class); initComponents(); // Set models guiTable.setTableHeader(null); guiTable.setModel(model); guiTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); guiTable.setRowMargin(0); guiTable.setColumnMargin(0); guiTable.getColumn(0).setCellRenderer(new SymbolizerRenderer()); guiTable.getColumn(1).setCellRenderer(new ActionCell.Renderer(ICO_UP)); guiTable.getColumn(1).setCellEditor(new ActionCell.Editor(ICO_UP) { @Override public void actionPerformed(final ActionEvent e, Object value) { final Symbolizer symbol = (Symbolizer) value; model.moveUp(symbol); } }); guiTable.getColumn(2).setCellRenderer(new ActionCell.Renderer(ICO_DOWN)); guiTable.getColumn(2).setCellEditor(new ActionCell.Editor(ICO_DOWN) { @Override public void actionPerformed(final ActionEvent e, Object value) { final Symbolizer symbol = (Symbolizer) value; model.moveDown(symbol); } }); guiTable.getColumn(3).setCellRenderer(new ActionCell.Renderer(ICO_DELETE)); guiTable.getColumn(3).setCellEditor(new ActionCell.Editor(ICO_DELETE) { @Override public void actionPerformed(final ActionEvent e, Object value) { final Symbolizer symbol = (Symbolizer) value; model.rule.symbolizers().remove(symbol); } }); final int width = 30; guiTable.getColumn(1).setMinWidth(width); guiTable.getColumn(1).setPreferredWidth(width); guiTable.getColumn(1).setMaxWidth(width); guiTable.getColumn(2).setMinWidth(width); guiTable.getColumn(2).setPreferredWidth(width); guiTable.getColumn(2).setMaxWidth(width); guiTable.getColumn(3).setMinWidth(width); guiTable.getColumn(3).setPreferredWidth(width); guiTable.getColumn(3).setMaxWidth(width); guiTable.setTableHeader(null); guiTable.setRowHeight(30); guiTable.setFillsViewportHeight(true); guiTable.setBackground(Color.WHITE); guiTable.setShowGrid(true); guiTable.setShowHorizontalLines(true); guiTable.setShowVerticalLines(false); guiTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent evt) { if(evt.getValueIsAdjusting()) return; if(currentEditor != null){ currentEditor.removePropertyChangeListener(changeListener); } guiScrollInfo.setViewportView(null); guiOverviewLabel.parse(null); // Get all selected items final int selectetRow = guiTable.getSelectedRow(); if (selectetRow >= 0 && model.rule.symbolizers().size() > selectetRow) { final Object item = model.rule.symbolizers().get(selectetRow); if (item != null) { currentEditor = StyleElementEditor.findEditor(item); if (currentEditor != null) { currentEditor.setLayer(layer); currentEditor.parse(item); currentEditor.addPropertyChangeListener(changeListener); guiScrollInfo.setViewportView(currentEditor); } guiOverviewLabel.parse(item); } } guiScrollInfo.revalidate(); guiScrollInfo.repaint(); } }); guiOverviewLabel.setMir(true); jAddButton.setIcon(ICO_ADD); } /** * 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() { jPanel3 = new JPanel(); jSplitPane1 = new JSplitPane(); jPanel1 = new JPanel(); jLabel5 = new JLabel(); jLabel7 = new JLabel(); guiOverviewLabel = new JPreview(); jAddButton = new JButton(); jScrollPane2 = new JScrollPane(); guiTable = new JXTable(); jPanel2 = new JPanel(); jLabel6 = new JLabel(); guiScrollInfo = new JScrollPane(); setMinimumSize(new Dimension(820, 400)); setLayout(new BorderLayout()); jSplitPane1.setDividerLocation(240); jLabel5.setFont(new Font("Lucida Grande", 1, 13)); // NOI18N jLabel5.setText(MessageBundle.format("symbolLayer")); // NOI18N jLabel7.setFont(new Font("Lucida Grande", 1, 13)); // NOI18N jLabel7.setText(MessageBundle.format("symbolPreview")); // NOI18N GroupLayout guiOverviewLabelLayout = new GroupLayout(guiOverviewLabel); guiOverviewLabel.setLayout(guiOverviewLabelLayout); guiOverviewLabelLayout.setHorizontalGroup( guiOverviewLabelLayout.createParallelGroup(Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) ); guiOverviewLabelLayout.setVerticalGroup( guiOverviewLabelLayout.createParallelGroup(Alignment.LEADING) .addGap(0, 109, Short.MAX_VALUE) ); jAddButton.setText(MessageBundle.format("addSymbol")); // NOI18N jAddButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { jAddButtonActionPerformed(evt); } }); jScrollPane2.setViewportView(guiTable); GroupLayout jPanel1Layout = new GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(Alignment.LEADING) .addGroup(Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(Alignment.TRAILING) .addComponent(jAddButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(guiOverviewLabel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jScrollPane2, Alignment.LEADING) .addGroup(Alignment.LEADING, jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(Alignment.LEADING) .addComponent(jLabel5) .addComponent(jLabel7)) .addGap(0, 99, Short.MAX_VALUE))) .addContainerGap()) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addGap(18, 18, 18) .addComponent(jLabel5) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(jScrollPane2, GroupLayout.DEFAULT_SIZE, 180, Short.MAX_VALUE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(jAddButton, GroupLayout.PREFERRED_SIZE, 23, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(jLabel7) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(guiOverviewLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); jSplitPane1.setLeftComponent(jPanel1); jLabel6.setFont(new Font("Lucida Grande", 1, 13)); // NOI18N jLabel6.setText(MessageBundle.format("editSymbol")); // NOI18N GroupLayout jPanel2Layout = new GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel6) .addContainerGap(478, Short.MAX_VALUE)) .addComponent(guiScrollInfo, Alignment.TRAILING) ); jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel6) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(guiScrollInfo, GroupLayout.DEFAULT_SIZE, 365, Short.MAX_VALUE)) ); jSplitPane1.setRightComponent(jPanel2); GroupLayout jPanel3Layout = new GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( jPanel3Layout.createParallelGroup(Alignment.LEADING) .addComponent(jSplitPane1) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(Alignment.LEADING) .addComponent(jSplitPane1) ); add(jPanel3, BorderLayout.CENTER); }// </editor-fold>//GEN-END:initComponents @Override public boolean canHandle(Object target) { return target instanceof MapLayer; } @Override public void setTarget(Object target) { if(target instanceof MapLayer){ setLayer((MapLayer)target); parse(target); } } @Override public void apply() { if(layer != null){ if(currentEditor!=null){ currentEditor.apply(); } if(parentRule==null){ layer.setStyle(getStyleFactory().style(model.rule.symbolizers().toArray(new Symbolizer[0]))); } } } @Override public void reset() { parse(parentRule); } @Override public String getTitle() { return "Simple"; } @Override public ImageIcon getIcon() { return IconBundle.getIcon("16_simple_style"); } @Override public Image getPreview() { return IconBundle.getIcon("preview_style_simple").getImage(); } @Override public String getToolTip() { return ""; } @Override public Component getComponent() { return this; } @Override public void setLayer(final MapLayer layer) { this.layer = layer; if(this.layer!=null){ layerListener.unregisterSource(this.layer); } this.layer = layer; if(this.layer!=null){ layerListener.registerSource(this.layer); } } @Override public MapLayer getLayer() { return layer; } @Override public void parse(final Object obj) { model.setRule(getStyleFactory().rule()); parentRule = null; if(layer != null){ for(final FeatureTypeStyle fts : layer.getStyle().featureTypeStyles()){ for(final Rule rule : fts.rules()){ parse((MutableRule)rule); break; //we only retrieve the first rule. } } } } private void parse(final MutableRule rule) { //listen to rule change from other syle editors if(parentRule!=rule){ parentRule = (MutableRule) rule; model.setRule(rule); } } @Override public Object create() { return style; } @Override protected Object[] getFirstColumnComponents() { return new Object[]{}; } private void jAddButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jAddButtonActionPerformed final List<Class> clazz = new ArrayList<Class>(); clazz.add(Symbolizer.class); clazz.add(PointSymbolizer.class); clazz.add(LineSymbolizer.class); clazz.add(PolygonSymbolizer.class); clazz.add(TextSymbolizer.class); clazz.add(RasterSymbolizer.class); bankController.setClazzList(clazz); final int result = JOptionDialog.show(this, bankController,JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) { if (bankController.getSelectedSymbol() != null) { model.rule.symbolizers().add( (Symbolizer)bankController.getSelectedSymbol()); final int index = model.rule.symbolizers().size()-1; guiTable.getSelectionModel().setSelectionInterval(index, index); } } }//GEN-LAST:event_jAddButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private JPreview guiOverviewLabel; private JScrollPane guiScrollInfo; private JXTable guiTable; private JButton jAddButton; private JLabel jLabel5; private JLabel jLabel6; private JLabel jLabel7; private JPanel jPanel1; private JPanel jPanel2; private JPanel jPanel3; private JScrollPane jScrollPane2; private JSplitPane jSplitPane1; // End of variables declaration//GEN-END:variables //style events @Override public void styleChange(MapLayer source, EventObject event) { //check if the first rule changed final MutableStyle ms = this.layer.getStyle(); if(ms.featureTypeStyles().isEmpty()){ parse(this.layer.getStyle()); return; } final MutableFeatureTypeStyle fts = ms.featureTypeStyles().get(0); if(fts.rules().isEmpty()){ parse(this.layer.getStyle()); return; } final MutableRule r = fts.rules().get(0); if(parentRule!=r){ parse(this.layer.getStyle()); return; } } @Override public void itemChange(CollectionChangeEvent<MapItem> event) {} @Override public void propertyChange(PropertyChangeEvent evt) { if(MapLayer.STYLE_PROPERTY.equals(evt.getPropertyName()) && evt.getOldValue()!=evt.getNewValue()){ parse(this.layer.getStyle()); } } private static final class SymbolizerModel extends AbstractTableModel implements RuleListener{ private MutableRule rule; private final Weak ruleListener = new Weak(this); public SymbolizerModel(MutableRule rule) { setRule(rule); } public void setRule(MutableRule rule) { ArgumentChecks.ensureNonNull("rule", rule); if(this.rule==rule) return; if(this.rule!=null){ this.ruleListener.unregisterSource(this.rule); } this.rule = rule; this.ruleListener.registerSource(rule); fireTableDataChanged(); } public void moveUp(final Symbolizer s) { int index = rule.symbolizers().indexOf(s); if (index != 0) { rule.symbolizers().remove(s); rule.symbolizers().add(index - 1, s); } } public void moveDown(final Symbolizer s) { int index = rule.symbolizers().indexOf(s); if (index != rule.symbolizers().size() - 1) { rule.symbolizers().remove(s); rule.symbolizers().add(index + 1, s); } } @Override public int getRowCount() { return rule.symbolizers().size(); } @Override public int getColumnCount() { return 4; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return columnIndex > 0; } @Override public Object getValueAt(int rowIndex, int columnIndex) { return rule.symbolizers().get(rowIndex); } @Override public void symbolizerChange(CollectionChangeEvent<Symbolizer> event) { final int type = event.getType(); if(type == CollectionChangeEvent.ITEM_ADDED){ fireTableRowsInserted((int)event.getRange().getMinDouble(), (int)event.getRange().getMaxDouble()); }else if(type == CollectionChangeEvent.ITEM_REMOVED){ fireTableRowsDeleted((int)event.getRange().getMinDouble(), (int)event.getRange().getMaxDouble()); }else if(type == CollectionChangeEvent.ITEM_CHANGED){ fireTableRowsUpdated((int)event.getRange().getMinDouble(), (int)event.getRange().getMaxDouble()); } } @Override public void propertyChange(PropertyChangeEvent evt) { } } private static class SymbolizerRenderer extends DefaultTableCellRenderer { private final JPreview preview = new JPreview(); @Override public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { final JLabel lbl = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (value instanceof Symbolizer) { Symbolizer symbol = (Symbolizer) value; preview.parse(symbol); if (symbol.getName() != null && !symbol.getName().isEmpty()) { lbl.setText(symbol.getName()); } else if (symbol.getDescription() != null && symbol.getDescription().getTitle() != null && !symbol.getDescription().getTitle().toString().isEmpty()) { lbl.setText(symbol.getDescription().getTitle().toString()); } else { lbl.setText("Unnamed"); } } preview.setSize(lbl.getHeight(), lbl.getHeight()); lbl.setIcon(new ImageIcon(preview.getImage())); return lbl; } } }