/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2007 - 2008, Open Source Geospatial Foundation (OSGeo) * (C) 2008 - 2010, Johann Sorel * * 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; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Image; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.sql.Time; import java.sql.Timestamp; import java.text.DateFormat; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.GroupLayout.Alignment; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import org.geotoolkit.cql.JCQLTextPane; import org.geotoolkit.data.FeatureCollection; import org.geotoolkit.data.FeatureStore; import org.geotoolkit.data.query.QueryBuilder; import org.geotoolkit.data.query.Selector; import org.geotoolkit.data.query.Source; import org.geotoolkit.data.session.Session; import org.geotoolkit.factory.FactoryFinder; import org.geotoolkit.filter.identity.DefaultFeatureId; import org.geotoolkit.gui.swing.util.JOptionDialog; import org.geotoolkit.gui.swing.util.LoadingLockableUI; import org.geotoolkit.gui.swing.propertyedit.featureeditor.PropertyValueEditor; import org.geotoolkit.gui.swing.propertyedit.featureeditor.TableCellEditorRenderer; import org.geotoolkit.gui.swing.propertyedit.model.FeatureCollectionModel; import org.geotoolkit.gui.swing.resource.MessageBundle; import org.geotoolkit.map.FeatureMapLayer; import org.geotoolkit.map.LayerListener; import org.geotoolkit.map.MapBuilder; import org.geotoolkit.map.MapItem; import org.geotoolkit.map.MapLayer; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.font.FontAwesomeIcons; import org.geotoolkit.font.IconBuilder; import org.geotoolkit.style.RandomStyleBuilder; import org.geotoolkit.util.collection.CollectionChangeEvent; import org.geotoolkit.version.Version; import org.geotoolkit.version.VersionHistory; import org.geotoolkit.version.Versioned; import org.geotoolkit.version.VersioningException; import org.jdesktop.jxlayer.plaf.ext.LockableUI; import org.jdesktop.swingx.JXErrorPane; 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.jdesktop.swingx.table.DatePickerCellEditor; import org.opengis.feature.AttributeType; import org.opengis.feature.Feature; import org.opengis.feature.FeatureType; import org.opengis.feature.PropertyType; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.Id; import org.opengis.filter.identity.Identifier; import org.openide.util.Exceptions; /** * layer feature panel * * @author Johann Sorel * @module */ public class LayerFeaturePropertyPanel extends AbstractPropertyPane implements LayerListener { private static final ImageIcon ICON_TABLE = IconBuilder.createIcon(FontAwesomeIcons.ICON_LIST, 16, FontAwesomeIcons.DEFAULT_COLOR); private static final ImageIcon ICON_VERSIONED = IconBuilder.createIcon(FontAwesomeIcons.ICON_CLOCK_O, 16, FontAwesomeIcons.DEFAULT_COLOR); public static final String ACTION_REF = "LayerFeaturePropertyPanel"; private final ListSelectionListener selectionListener = new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent arg0) { updateTableSelection(); } }; private final TableModelListener tableListener = new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { checkChanges(); } }; private final List<Action> actions = new ArrayList<Action>(); private final LockableUI lockableUI = new LoadingLockableUI(); private final JCQLTextPane guiCQL = new JCQLTextPane(); private FeatureMapLayer layer = null; private boolean editable = false; private final LayerListener.Weak weakListener = new LayerListener.Weak(this); private final List<PropertyValueEditor> editors = new CopyOnWriteArrayList<>(); private final JXTable tab_data = new JXTable(){ @Override public TableCellEditor getCellEditor(final int row, final int column) { final FeatureCollectionModel model = (FeatureCollectionModel) tab_data.getModel(); final int modelindex = tab_data.getColumnModel().getColumn(column).getModelIndex(); final PropertyType desc = model.getColumnDesc(modelindex); if(desc != null){ final PropertyValueEditor edit = JAttributeEditor.getEditor(editors, (AttributeType) desc); if(edit != null){ return new TableCellEditorRenderer.Editor(edit); } } return super.getCellEditor(row, column); } }; private final JFeatureCollectionOutline outline = new JFeatureCollectionOutline(); /** Creates new form DefaultMapLayerTablePanel */ public LayerFeaturePropertyPanel() { super(MessageBundle.format("property_feature_table"),ICON_TABLE,null,MessageBundle.format("crs")); initComponents(); JLabel label = new JLabel(MessageBundle.format("cql_text_help")); guiPanFilter.add(BorderLayout.NORTH, label); guiPanFilter.add(BorderLayout.CENTER,guiCQL); tab_data.setEditable(false); tab_data.setColumnControlVisible(true); tab_data.setHorizontalScrollEnabled(true); tab_data.setAutoResizeMode(JXTable.AUTO_RESIZE_OFF); tab_data.getSelectionModel().addListSelectionListener(selectionListener); tab_data.setHighlighters(new Highlighter[]{HighlighterFactory.createAlternateStriping(Color.white, HighlighterFactory.QUICKSILVER, 5)}); tab_data.setShowGrid(true, true); tab_data.setGridColor(Color.GRAY.brighter()); tab_data.setDefaultEditor(Date.class, new DatePickerCellEditor(DateFormat.getDateTimeInstance())); tab_data.setDefaultEditor(java.sql.Date.class, new DatePickerCellEditor(DateFormat.getDateTimeInstance())); tab_data.setDefaultEditor(Time.class, new DatePickerCellEditor(DateFormat.getDateTimeInstance())); tab_data.setDefaultEditor(Timestamp.class, new DatePickerCellEditor(DateFormat.getDateTimeInstance())); tab_data.setDefaultEditor(Versioned.class, new VersionEditor()); tab_data.setDefaultRenderer(Versioned.class, new VersionEditor()); editors.addAll(JAttributeEditor.createDefaultEditorList()); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tab_data.packAll(); } }); final JPopupMenu menu = new DynamicMenu(); jbu_action.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent event) { if(event.getButton() == MouseEvent.BUTTON1){ menu.show(jbu_action, event.getX()-10, event.getY()-10); } } @Override public void mousePressed(MouseEvent arg0) {} @Override public void mouseReleased(MouseEvent arg0) {} @Override public void mouseEntered(MouseEvent arg0) {} @Override public void mouseExited(MouseEvent arg0) {} }); checkChanges(); } /** * @return live list of property editors. */ public List<PropertyValueEditor> getEditors() { return editors; } /** * Check if there are changes in the current session and * activate commit/rollback buttons. */ private void checkChanges(){ boolean changes = false; if(layer != null){ final FeatureCollection col = layer.getCollection(); final Session session = col.getSession(); if(session != null){ changes = session.hasPendingChanges(); } } guiCommit.setEnabled(changes); guiRollback.setEnabled(changes); } private void updateLayerSelection(){ tab_data.getSelectionModel().removeListSelectionListener(selectionListener); weakListener.unregisterSource(layer); FeatureCollectionModel model = (FeatureCollectionModel) tab_data.getModel(); Id selection = layer.getSelectionFilter(); String selected = "0"; tab_data.getSelectionModel().clearSelection(); if(selection != null){ for(int i=0,n=tab_data.getRowCount();i<n;i++){ Feature f = model.getFeatureAt(i); if(selection.evaluate(f)){ int viewIndex = tab_data.convertRowIndexToView(i); tab_data.getSelectionModel().addSelectionInterval(viewIndex, viewIndex); } } selected = String.valueOf(selection.getIDs().size()); } guiCount.setText("Selection : "+ selected +" / "+tab_data.getModel().getRowCount()); weakListener.registerSource(layer); tab_data.getSelectionModel().addListSelectionListener(selectionListener); } private void updateTableSelection(){ tab_data.getSelectionModel().removeListSelectionListener(selectionListener); weakListener.unregisterSource(layer); final int[] rows = tab_data.getSelectedRows(); FeatureCollectionModel model = (FeatureCollectionModel) tab_data.getModel(); for(int i=0; i<rows.length; i++){ rows[i] = tab_data.convertRowIndexToModel(rows[i]); } final HashSet<Identifier> ids = new HashSet<Identifier>(); for(int i : rows){ ids.add(new DefaultFeatureId((String)model.getValueAt(i, 1))); } if(ids.isEmpty()){ layer.setSelectionFilter(null); }else{ FilterFactory ff = FactoryFinder.getFilterFactory(null); layer.setSelectionFilter(ff.id(ids)); } weakListener.registerSource(layer); tab_data.getSelectionModel().addListSelectionListener(selectionListener); } public List<Action> actions(){ return actions; } /** 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. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jPanel2 = new JPanel(); guiQueryButton = new JButton(); guiPanFilter = new JPanel(); jPanel4 = new JPanel(); jPanel3 = new JPanel(); jLabel1 = new JLabel(); guiVersions = new JComboBox(); jPanel1 = new JPanel(); jcb_edit = new JCheckBox(); guiCount = new JLabel(); guiCommit = new JButton(); guiRollback = new JButton(); jbu_action = new JButton(); guiShowId = new JCheckBox(); panCenter = new JPanel(); setLayout(new BorderLayout()); jPanel2.setLayout(new BorderLayout()); guiQueryButton.setText(MessageBundle.format("ok")); // NOI18N guiQueryButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { guiQueryButtonActionPerformed(evt); } }); jPanel2.add(guiQueryButton, BorderLayout.EAST); guiPanFilter.setPreferredSize(new Dimension(10, 0)); guiPanFilter.setLayout(new BorderLayout()); jPanel2.add(guiPanFilter, BorderLayout.CENTER); jPanel4.setLayout(new GridLayout(2, 1)); jLabel1.setHorizontalAlignment(SwingConstants.CENTER); jLabel1.setText(MessageBundle.format("version")); // NOI18N GroupLayout jPanel3Layout = new GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( jPanel3Layout.createParallelGroup(Alignment.LEADING) .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(jLabel1) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(Alignment.LEADING) .addComponent(jLabel1, GroupLayout.DEFAULT_SIZE, 25, Short.MAX_VALUE) ); jPanel4.add(jPanel3); jPanel4.add(guiVersions); jPanel2.add(jPanel4, BorderLayout.WEST); add(jPanel2, BorderLayout.NORTH); jPanel1.setOpaque(false); jcb_edit.setText(MessageBundle.format("property_edit")); // NOI18N jcb_edit.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); jcb_edit.setEnabled(false); jcb_edit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { actionEditer(evt); } }); guiCount.setHorizontalAlignment(SwingConstants.CENTER); guiCount.setText(" "); guiCommit.setText(MessageBundle.format("commit")); // NOI18N guiCommit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { guiCommitActionPerformed(evt); } }); guiRollback.setText(MessageBundle.format("rollback")); // NOI18N guiRollback.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { guiRollbackActionPerformed(evt); } }); jbu_action.setText(MessageBundle.format("property_action")); // NOI18N guiShowId.setText(MessageBundle.format("show_id")); // NOI18N guiShowId.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { guiShowIdActionPerformed(evt); } }); GroupLayout jPanel1Layout = new GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addComponent(guiCount) .addPreferredGap(ComponentPlacement.RELATED, 109, Short.MAX_VALUE) .addComponent(jcb_edit) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(guiShowId) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(guiCommit) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(guiRollback) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(jbu_action)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(Alignment.LEADING) .addGroup(Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(Alignment.LEADING) .addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE) .addComponent(jcb_edit) .addComponent(guiShowId) .addComponent(guiCount)) .addGroup(jPanel1Layout.createParallelGroup(Alignment.BASELINE) .addComponent(jbu_action, GroupLayout.PREFERRED_SIZE, 27, GroupLayout.PREFERRED_SIZE) .addComponent(guiRollback) .addComponent(guiCommit))) .addContainerGap()) ); jPanel1Layout.linkSize(SwingConstants.VERTICAL, new Component[] {guiCommit, guiCount, guiRollback, guiShowId, jbu_action, jcb_edit}); add(jPanel1, BorderLayout.SOUTH); panCenter.setLayout(new BorderLayout()); add(panCenter, BorderLayout.CENTER); }// </editor-fold>//GEN-END:initComponents private void actionEditer(final ActionEvent evt) {//GEN-FIRST:event_actionEditer tab_data.setEditable(((JCheckBox) evt.getSource()).isSelected()); }//GEN-LAST:event_actionEditer private void guiCommitActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiCommitActionPerformed if(layer != null){ final FeatureMapLayer candidate = layer; new Thread(){ @Override public void run() { lockableUI.setLocked(true); guiCommit.setEnabled(false); guiRollback.setEnabled(false); try { candidate.getCollection().getSession().commit(); } catch (DataStoreException ex) { JXErrorPane.showDialog(ex); }finally { lockableUI.setLocked(false); checkChanges(); } } }.start(); } }//GEN-LAST:event_guiCommitActionPerformed private void guiRollbackActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiRollbackActionPerformed if(layer != null){ final FeatureMapLayer candidate = layer; new Thread(){ @Override public void run() { lockableUI.setLocked(true); guiCommit.setEnabled(false); guiRollback.setEnabled(false); candidate.getCollection().getSession().rollback(); lockableUI.setLocked(false); reset(); } }.start(); } }//GEN-LAST:event_guiRollbackActionPerformed private void guiShowIdActionPerformed(final ActionEvent evt) {//GEN-FIRST:event_guiShowIdActionPerformed setTarget(layer); }//GEN-LAST:event_guiShowIdActionPerformed private void guiQueryButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_guiQueryButtonActionPerformed try { panCenter.removeAll(); Filter f = guiCQL.getFilter(); final QueryBuilder qb = new QueryBuilder(layer.getCollection().getFeatureType().getName().toString()); qb.setFilter(f); if(guiVersions.getSelectedItem()!=null && !(guiVersions.getSelectedItem() instanceof String)){ final Date d = ((Version)guiVersions.getSelectedItem()).getDate(); qb.setVersionDate(d); } final FeatureCollection subcol = layer.getCollection().subCollection(qb.buildQuery()); FeatureMapLayer layer = MapBuilder.createFeatureLayer(subcol, RandomStyleBuilder.createDefaultRasterStyle()); final FeatureCollectionModel m = new FeatureCollectionModel(tab_data, layer, guiShowId.isSelected()); tab_data.setModel(m); tab_data.packAll(); tab_data.getModel().addTableModelListener(tableListener); updateLayerSelection(); panCenter.add(BorderLayout.CENTER, new JScrollPane(tab_data)); panCenter.revalidate(); revalidate(); repaint(); checkChanges(); } catch (Exception ex) { ex.printStackTrace(); } }//GEN-LAST:event_guiQueryButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private JButton guiCommit; private JLabel guiCount; private JPanel guiPanFilter; private JButton guiQueryButton; private JButton guiRollback; private JCheckBox guiShowId; private JComboBox guiVersions; private JLabel jLabel1; private JPanel jPanel1; private JPanel jPanel2; private JPanel jPanel3; private JPanel jPanel4; private JButton jbu_action; private JCheckBox jcb_edit; private JPanel panCenter; // End of variables declaration//GEN-END:variables @Override public void setTarget(final Object target) { if(layer != null){ weakListener.unregisterSource(layer); tab_data.getModel().removeTableModelListener(tableListener); } panCenter.removeAll(); if (target instanceof FeatureMapLayer) { layer = (FeatureMapLayer) target; final FeatureCollection source = layer.getCollection(); editable = source.isWritable(); jcb_edit.setEnabled(editable); final FeatureType type = source.getFeatureType(); if(type.isSimple()){ //use table view final FeatureCollectionModel m = new FeatureCollectionModel(tab_data, layer, guiShowId.isSelected()); tab_data.setModel(m); tab_data.packAll(); tab_data.getModel().addTableModelListener(tableListener); updateLayerSelection(); panCenter.add(BorderLayout.CENTER, new JScrollPane(tab_data)); }else{ //use outline view outline.setTarget(source); panCenter.add(BorderLayout.CENTER, outline); } weakListener.registerSource(layer); //list versions final Source src = source.getSource(); final List lst = new ArrayList(); lst.add("-"); if(src instanceof Selector){ final Selector s = (Selector) src; final FeatureStore store = s.getSession().getFeatureStore(); if(store.getQueryCapabilities().handleVersioning()){ try { final VersionHistory history = store.getVersioning(source.getFeatureType().getName().toString()); lst.addAll(history.list()); } catch (VersioningException ex) { Exceptions.printStackTrace(ex); } } } guiVersions.setModel(new ListComboBoxModel(lst)); if (lst.size() < 2) { guiVersions.setVisible(false); jLabel1.setVisible(false); } else { guiVersions.setVisible(true); } } guiCommit.setVisible(false); guiRollback.setVisible(false); panCenter.revalidate(); revalidate(); repaint(); checkChanges(); } public void updateEditPane(){ } public FeatureMapLayer getTarget(){ return layer; } @Override public boolean canHandle(Object target) { return target instanceof FeatureMapLayer; } @Override public void apply() { } @Override public void reset() { setTarget(getTarget()); } private class DynamicMenu extends JPopupMenu{ public DynamicMenu() { } @Override public void setVisible(final boolean visible) { DynamicMenu.this.removeAll(); if(visible){ for(final Action item : actions){ item.putValue(ACTION_REF, LayerFeaturePropertyPanel.this); final JFeaturePanelAction jfa = new JFeaturePanelAction(item); jfa.setFeaturePanel(LayerFeaturePropertyPanel.this); DynamicMenu.this.add(jfa); } } super.setVisible(visible); } } //////////////////////////////////////////////////////////////////////////// //Layer listener /////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// @Override public void propertyChange(final PropertyChangeEvent event) { if(MapLayer.SELECTION_FILTER_PROPERTY.equals(event.getPropertyName())){ updateLayerSelection(); } } @Override public void styleChange(final MapLayer source, final EventObject event) { } @Override public void itemChange(final CollectionChangeEvent<MapItem> event) { } private class VersionEditor extends AbstractCellEditor implements TableCellRenderer,TableCellEditor{ private final JLabel lbl = new JLabel(ICON_VERSIONED); public VersionEditor() { } @Override public Object getCellEditorValue() { return null; } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if(value instanceof Versioned){ return new VersionButton((Versioned)value); }else{ return new JLabel(""); } } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { if(value instanceof Versioned){ return new VersionButton((Versioned)value); }else{ return new JLabel(""); } } } private class VersionButton extends JButton implements ActionListener{ private Versioned versioned; public VersionButton(Versioned v) { super(ICON_VERSIONED); setMargin(new Insets(0, 0, 0, 0)); this.versioned = v; addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { final JComboBox jcb = new JComboBox(); try{ ComboBoxModel m = new ListComboBoxModel(versioned.getHistory().list()); jcb.setModel(m); }catch(Exception ex){ ex.printStackTrace(); } final JFeatureOutLine editor = new JFeatureOutLine(); editor.setEnabled(false); jcb.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { final Version v = (Version) jcb.getSelectedItem(); try{ Feature o = (Feature) versioned.getForVersion(v); editor.setEdited(o); }catch(Exception ex){ ex.printStackTrace(); editor.setEdited((Feature)null); } } }); final Version v = (Version) jcb.getSelectedItem(); if(v!=null){ try{ Feature o = (Feature) versioned.getForVersion(v); editor.setEdited(o); }catch(Exception ex){ ex.printStackTrace(); editor.setEdited((Feature)null); } } final JPanel p = new JPanel(new BorderLayout()); p.add(BorderLayout.NORTH,jcb); p.add(BorderLayout.CENTER,new JScrollPane(editor)); JOptionDialog.show(VersionButton.this, p, JOptionPane.OK_OPTION); } } }