/* * This file is part of NavTable * Copyright (C) 2009 - 2010 Cartolab (Universidade da Coru�a) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Authors: * Juan Ignacio Varela Garc�a <nachouve (at) gmail (dot) com> * Pablo Sanxiao Roca <psanxiao (at) gmail (dot) com> * Javier Est�vez Vali�as <valdaris (at) gmail (dot) com> * Andres Maneiro <andres.maneiro@gmail.com> * Jorge Lopez Fernandez <jlopez (at) cartolab (dot) es> */ package es.udc.cartolab.gvsig.navtable; import java.awt.BorderLayout; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.sql.Types; import java.util.Vector; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import org.apache.log4j.Logger; import com.hardcode.gdbms.driver.exceptions.ReadDriverException; import com.hardcode.gdbms.engine.values.Value; import com.iver.andami.PluginServices; import com.iver.andami.ui.mdiManager.WindowInfo; import com.iver.cit.gvsig.exceptions.visitors.StopWriterVisitorException; import com.iver.cit.gvsig.fmap.core.IGeometry; import com.iver.cit.gvsig.fmap.layers.FLyrVect; import com.iver.cit.gvsig.fmap.layers.ReadableVectorial; import com.iver.cit.gvsig.fmap.layers.SelectableDataSource; import com.iver.cit.gvsig.fmap.layers.VectorialDBAdapter; import com.iver.cit.gvsig.fmap.layers.VectorialFileAdapter; import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData; import com.iver.utiles.extensionPoints.ExtensionPoint; import com.iver.utiles.extensionPoints.ExtensionPoints; import com.iver.utiles.extensionPoints.ExtensionPointsSingleton; import com.vividsolutions.jts.geom.Geometry; import es.udc.cartolab.gvsig.navtable.format.ValueFormatNT; import es.udc.cartolab.gvsig.navtable.listeners.MyMouseListener; import es.udc.cartolab.gvsig.navtable.listeners.PositionEvent; import es.udc.cartolab.gvsig.navtable.preferences.Preferences; import es.udc.cartolab.gvsig.navtable.table.AttribTableCellRenderer; import es.udc.cartolab.gvsig.navtable.table.NavTableModel; /** * <p> * NavTable's main panel is a two-column table that shows data row of the layer. * The first column contains the attributes names and the second one contains * its values. * </p> * * <img src="images/NavTable.png" /> * * <p> * NOTE: the <i>data table</i> is the original data storage, not the table to be * shown in this window. * </p> * * @author Nacho Varela * @author Pablo Sanxiao * @author Andres Maneiro * @author Jorge Lopez * @author Francisco Puga */ public class NavTable extends AbstractNavTable { private static final Logger logger = Logger.getLogger(NavTable.class); private static final long serialVersionUID = 1L; private boolean isFillingValues = false; private boolean isSavingValues = false; protected JTable table = null; private AttribTableCellRenderer cellRenderer = null; private MyTableModelListener myTableModelListener; private MyKeyListener myKeyListener; private MyMouseListener myMouseListener; private final ValueFormatNT valueFormatNT = new ValueFormatNT(); // Mouse buttons constants public static final int BUTTON_RIGHT = 3; public NavTable(FLyrVect layer) { super(layer); } @Deprecated public NavTable(SelectableDataSource recordset, String tableName) { super(recordset, tableName); } public boolean isFillingValues() { return isFillingValues; } public void setFillingValues(boolean isFillingValues) { this.isFillingValues = isFillingValues; } /** * It creates a panel with a table that shows the data linked to a feature * of the layer. Each row is a attribute-value pair. * * @return the panel. */ @Override public JPanel getCenterPanel() { NavTableModel model = new NavTableModel(); table = new JTable(model); table.getTableHeader().setReorderingAllowed(false); myKeyListener = new MyKeyListener(); table.addKeyListener(myKeyListener); myMouseListener = new MyMouseListener(this); table.addMouseListener(myMouseListener); this.cellRenderer = new AttribTableCellRenderer(); model.addColumn(PluginServices.getText(this, "headerTableAttribute")); model.addColumn(PluginServices.getText(this, "headerTableValue")); TableColumn attribColumn = table.getColumn(PluginServices.getText(this, "headerTableAttribute")); attribColumn.setCellRenderer(this.cellRenderer); attribColumn = table.getColumn(PluginServices.getText(this, "headerTableValue")); attribColumn.setCellRenderer(this.cellRenderer); myTableModelListener = new MyTableModelListener(); model.addTableModelListener(myTableModelListener); JScrollPane scrollPane = new JScrollPane(table); centerPanel = new JPanel(new BorderLayout()); centerPanel.add(scrollPane, BorderLayout.CENTER); return centerPanel; } class MyKeyListener implements KeyListener { @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { // TODO If control + cursor ---> Inicio / Fin if (e.getKeyCode() == KeyEvent.VK_RIGHT) { next(); } if (e.getKeyCode() == KeyEvent.VK_LEFT) { before(); } if (e.getKeyCode() == KeyEvent.VK_HOME) { first(); } if (e.getKeyCode() == KeyEvent.VK_END) { last(); } } @Override public void keyTyped(KeyEvent e) { } } class MyTableModelListener implements TableModelListener { @Override public void tableChanged(TableModelEvent e) { if (e.getType() == TableModelEvent.UPDATE && !isFillingValues()) { setChangedValues(); enableSaveButton(isChangedValues()); } } } protected boolean isEditing() { return layer.isEditing(); } @Deprecated // deprecated by fpuga, 28/02/2014 protected void updateValue(int row, int col, String newValue) { ToggleEditing te = new ToggleEditing(); try { te.modifyValue(layer, row, col, newValue); } catch (Exception e) { e.printStackTrace(); } } @Override protected void registerNavTableButtonsOnActionToolBarExtensionPoint() { /* * TODO: this will make the extension point mechanims not work as * expected for navtable extension. It only will add the regular buttons * navtable has, not all included in the extensionpoint (as the default * behaviour is). * * Probably we need to get rid of extensionpoints mechanism as -roughly- * it is a global variable mechanism, which is not what we need. For * action buttons, it'll be desirable a mechanism that: * * 1) allow adding buttons for custom forms build on abstractnavtable. * 2) don't share those buttons between all children of * abstractnavtable, unless requested otherwise. * * Check decorator pattern, as it seems to fit well here. */ ExtensionPoints extensionPoints = ExtensionPointsSingleton .getInstance(); ExtensionPoint ep = (ExtensionPoint) extensionPoints .get(AbstractNavTable.NAVTABLE_ACTIONS_TOOLBAR); if (ep != null) { ep.clear(); } super.registerNavTableButtonsOnActionToolBarExtensionPoint(); } /** * It gets the alias name of the attributes in the alias file if exists * * @param fieldName * @return alias */ private String getAlias(String fieldName) { File layerFile = null; String filePath = null; String alias = fieldName; String pathToken = null; File fileAlias = null; // Added to tables without Layer support, but must be supported alias // here also if (layer == null) { pathToken = dataName.contains(".") ? Preferences.getAliasDir() + File.separator + dataName.substring(0, dataName.lastIndexOf(".")) : Preferences.getAliasDir() + File.separator + dataName; fileAlias = new File(pathToken + ".alias"); } else { ReadableVectorial source = layer.getSource(); if (source != null && source instanceof VectorialFileAdapter) { layerFile = ((VectorialFileAdapter) source).getFile(); filePath = layerFile.getAbsolutePath(); pathToken = filePath.substring(0, filePath.lastIndexOf(".")); fileAlias = new File(pathToken + ".alias"); if (!fileAlias.exists()) { pathToken = Preferences.getAliasDir() + File.separator + layer.getName(); fileAlias = new File(pathToken + ".alias"); } } else if (source instanceof VectorialDBAdapter) { pathToken = Preferences.getAliasDir() + File.separator + layer.getName(); fileAlias = new File(pathToken + ".alias"); } else { return fieldName; } } if (!fileAlias.exists()) { return fieldName; } BufferedReader fileReader = null; try { String line; fileReader = new BufferedReader(new FileReader(fileAlias)); while ((line = fileReader.readLine()) != null) { String tokens[] = line.split("="); if (tokens.length == 2) { String attrName = tokens[0].toUpperCase(); if (fieldName.toUpperCase().compareTo(attrName) == 0) { alias = tokens[1]; break; } } } } catch (FileNotFoundException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } finally { try { if (fileReader != null) { fileReader.close(); } } catch (IOException e) { logger.error(e.getStackTrace(), e); } } return alias; } @Override protected void initWidgets() { try { DefaultTableModel model = (DefaultTableModel) table.getModel(); model.setRowCount(0); Vector<String> aux = null; SelectableDataSource sds = getRecordset(); for (int i = 0; i < sds.getFieldCount(); i++) { String attName = sds.getFieldName(i); aux = new Vector<String>(2); aux.add(getAlias(attName)); aux.add(" "); model.addRow(aux); } if (layer != null) { // Geom_LENGTH aux = new Vector<String>(2); aux.add(PluginServices.getText(this, "Geom_LENGTH")); aux.add("0.0"); model.addRow(aux); // Geom_AREA aux = new Vector<String>(2); aux.add(PluginServices.getText(this, "Geom_AREA")); aux.add("0.0"); model.addRow(aux); this.cellRenderer.emptyNoEditableRows(); this.cellRenderer.addNoEditableRow(model.getRowCount() - 2); this.cellRenderer.addNoEditableRow(model.getRowCount() - 1); } } catch (ReadDriverException e) { logger.error(e.getMessage(), e); } } @Override public void fillEmptyValues() { setFillingValues(true); DefaultTableModel model = (DefaultTableModel) table.getModel(); for (int i = 0; i < model.getRowCount(); i++) { model.setValueAt("", i, 1); } setFillingValues(false); } @Override public boolean isSavingValues() { return isSavingValues; } public void setSavingValues(boolean bool) { isSavingValues = bool; } @Override public void fillValues() { SelectableDataSource sds; try { sds = getRecordset(); setFillingValues(true); DefaultTableModel model = (DefaultTableModel) table.getModel(); for (int i = 0; i < sds.getFieldCount(); i++) { String textoValue = sds.getFieldValue(getPosition(), i) .getStringValue(valueFormatNT); model.setValueAt(textoValue, i, 1); } if (layer != null && layer instanceof AlphanumericData) { // Fill GEOM_LENGTH String value = "0.0"; IGeometry g; ReadableVectorial source = (layer).getSource(); source.start(); g = source.getShape(new Long(getPosition()).intValue()); source.stop(); if (g == null) { model.setValueAt("0", sds.getFieldCount(), 1); model.setValueAt("0", sds.getFieldCount() + 1, 1); return; } Geometry geom = g.toJTSGeometry(); // TODO Format number (Set units in Preferences) value = String.valueOf(Math.round(geom.getLength())); model.setValueAt(value, sds.getFieldCount(), 1); // Fill GEOM_AREA value = "0.0"; source.start(); g = source.getShape(new Long(getPosition()).intValue()); source.stop(); geom = g.toJTSGeometry(); // TODO Format number (Set units in Preferences) value = String.valueOf(Math.round(geom.getArea())); model.setValueAt(value, sds.getFieldCount() + 1, 1); } } catch (ReadDriverException e) { logger.error(e.getMessage(), e); } finally { setFillingValues(false); } } protected Vector<Integer> getChangedValues() { Vector<Integer> changedValues = new Vector<Integer>(); DefaultTableModel model = (DefaultTableModel) table.getModel(); try { SelectableDataSource sds = getRecordset(); for (int i = 0; i < sds.getFieldCount(); i++) { String tableValue = model.getValueAt(i, 1).toString(); Value value = sds.getFieldValue(getPosition(), i); String layerValue = value.getStringValue(valueFormatNT); if (tableValue.compareTo(layerValue) != 0) { changedValues.add(new Integer(i)); } } } catch (ReadDriverException e) { logger.error(e.getMessage(), e); } return changedValues; } protected void setChangedValues() { Vector<Integer> changedValues = getChangedValues(); if (changedValues.size() > 0) { setChangedValues(true); } else { setChangedValues(false); } } @Override public void selectRow(int row) { table.setRowSelectionInterval(row, row); } protected boolean isSaveable() { stopCellEdition(); if (layer.isWritable()) { return true; } else { JOptionPane.showMessageDialog(this, String.format( PluginServices.getText(this, "non_editable"), layer.getName())); return false; } } protected int[] getIndexes() { Vector<Integer> changedValues = getChangedValues(); int[] attIndexes = new int[changedValues.size()]; if (isChangedValues()) { DefaultTableModel model = (DefaultTableModel) table.getModel(); int j = 0; for (int i = 0; i < model.getRowCount(); i++) { if (changedValues.contains(new Integer(i))) { attIndexes[j] = i; j++; } } } return attIndexes; } protected String[] getValues() { Vector<Integer> changedValues = getChangedValues(); String[] attValues = new String[changedValues.size()]; if (isChangedValues()) { DefaultTableModel model = (DefaultTableModel) table.getModel(); int j = 0; for (int i = 0; i < model.getRowCount(); i++) { if (changedValues.contains(new Integer(i))) { try { Object value = model.getValueAt(i, 1); attValues[j] = value.toString(); if (getRecordset().getFieldType(i) == Types.DATE) { attValues[j] = attValues[j].replaceAll("-", "/"); } } catch (ReadDriverException e) { logger.error(e.getMessage(), e); } finally { j++; } } } } return attValues; } @Override public boolean saveRecord() throws StopWriterVisitorException { if (isSaveable()) { setSavingValues(true); int[] attIndexes = getIndexes(); String[] attValues = getValues(); int currentPos = Long.valueOf(getPosition()).intValue(); try { ToggleEditing te = new ToggleEditing(); boolean wasEditing = layer.isEditing(); if (!wasEditing) { te.startEditing(layer); } te.modifyValues(layer, currentPos, attIndexes, attValues); if (!wasEditing) { te.stopEditing(layer, false); } setChangedValues(false); return true; } catch (StopWriterVisitorException e) { setSavingValues(false); throw e; } finally { setSavingValues(false); } } return false; } /** * It stops the row editing when the save button is pressed. * */ protected void stopCellEdition() { if (table.isEditing()) { if (table.getCellEditor() != null) { table.getCellEditor().stopCellEditing(); } } } @Override public void beforePositionChange(PositionEvent e) { stopCellEdition(); super.beforePositionChange(e); } @Override public void windowClosed() { stopCellEdition(); this.table.getModel().removeTableModelListener(myTableModelListener); this.table.removeKeyListener(myKeyListener); super.windowClosed(); } @Override public Object getWindowProfile() { return WindowInfo.PROPERTIES_PROFILE; } @Override public void reloadRecordset() throws ReadDriverException { super.reloadRecordset(); initWidgets(); } public JTable getTable() { return this.table; } }