/*
* 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>
* Jorge Lopez Fernandez <jlopez (at) cartolab (dot) es>
*/
package es.udc.cartolab.gvsig.navtable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.RowSorter.SortKey;
import net.miginfocom.swing.MigLayout;
import org.apache.log4j.Logger;
import org.gvsig.exceptions.BaseException;
import com.hardcode.gdbms.driver.exceptions.InitializeDriverException;
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
import com.iver.andami.PluginServices;
import com.iver.andami.ui.mdiFrame.MDIFrame;
import com.iver.andami.ui.mdiManager.IWindow;
import com.iver.andami.ui.mdiManager.IWindowListener;
import com.iver.andami.ui.mdiManager.WindowInfo;
import com.iver.cit.gvsig.FiltroExtension;
import com.iver.cit.gvsig.exceptions.visitors.StopWriterVisitorException;
import com.iver.cit.gvsig.fmap.core.IGeometry;
import com.iver.cit.gvsig.fmap.edition.AfterFieldEditEvent;
import com.iver.cit.gvsig.fmap.edition.EditionEvent;
import com.iver.cit.gvsig.fmap.layers.FBitSet;
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
import com.iver.cit.gvsig.fmap.layers.LayerEvent;
import com.iver.cit.gvsig.fmap.layers.ReadableVectorial;
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
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 es.icarto.gvsig.navtable.navigation.NavigationHandler;
import es.udc.cartolab.gvsig.navtable.dataacces.IController;
import es.udc.cartolab.gvsig.navtable.dataacces.LayerController;
import es.udc.cartolab.gvsig.navtable.listeners.PositionEvent;
import es.udc.cartolab.gvsig.navtable.listeners.PositionListener;
import es.udc.cartolab.gvsig.navtable.preferences.Preferences;
import es.udc.cartolab.gvsig.navtable.utils.EditionListener;
/**
* <img src="images/NavTableWindow.png">
*
* If there is an image on
* 'gvSIG/extensiones/es.udc.cartolab.gvsig.navtable/images/navtable_header.png'
* it will be loaded on the NorthPanel.
*
*/
public abstract class AbstractNavTable extends JPanel implements IWindow,
ActionListener, IWindowListener, PositionListener {
private static final Logger logger = Logger
.getLogger(AbstractNavTable.class);
public static final int EMPTY_REGISTER = -1;
protected static final int BUTTON_REMOVE = 0;
protected static final int BUTTON_SAVE = 1;
protected static final int BUTTON_SELECTION = 2;
protected static final int BUTTON_ZOOM = 3;
protected static final int BUTTON_COPY_PREVIOUS = 4;
protected static final int BUTTON_COPY_SELECTED = 5;
private static final long serialVersionUID = 1L;
protected String deleteMessageKey = "confirm_delete_register";
protected String saveErrorTitleKey = "save_layer_error";
protected String saveErrorGenericMessageKey = "errorSavingData";
public static final String NAVTABLE_ACTIONS_TOOLBAR = "navtable_extension_point_actions_toolbar";
public static final String NAVTABLE_CONTEXT_MENU = "navtable_extension_point_context_menu";
protected JPanel northPanel = null;
protected JPanel centerPanel = null;
protected JPanel southPanel = null;
protected IController layerController;
private NavigationHandler navigation;
protected FLyrVect layer = null;
protected String dataName = "";
protected boolean changedValues = false;
// NORTH
protected JCheckBox fixScaleCB = null;
protected JCheckBox alwaysZoomCB = null;
// SOUTH
// actions buttons
protected JButton filterB = null;
protected JButton copyPreviousB = null;
protected JButton copySelectedB = null;
protected JButton zoomB = null;
protected JButton saveB = null;
protected JButton removeB = null;
protected JButton undoB = null;
// private boolean isSomeNavTableFormOpen = false;
protected EditionListener listener;
private JPanel actionsToolBar;
private JPanel optionsPanel;
protected boolean openEmptyLayers = false;
protected boolean isAlphanumericNT = false;
protected WindowInfo windowInfo = null;
public AbstractNavTable(FLyrVect layer) {
super();
this.layer = layer;
this.dataName = layer.getName();
navigation = new NavigationHandler(this);
}
// [nachouve] Check this method because
// When the table is on edition a weird identify
// is shown the title window instead of the layer name.
// Maybe can be set as deprecated and be replaced by:
// {@link AbstractNavTable(SelectableDataSource, String)}
// with a properly name as string parameter.
@Deprecated
public AbstractNavTable(SelectableDataSource recordset) {
this(recordset, recordset.getName());
}
/**
* Constructor of the class. This constructor is used by
* AlphanumericNavTable
*/
@Deprecated
public AbstractNavTable(SelectableDataSource recordset, String tableName) {
this(tableName);
}
public AbstractNavTable(String tableName) {
super();
this.dataName = tableName;
}
public boolean isAlphanumericNT() {
return this.isAlphanumericNT;
}
public boolean init() {
try {
if ((!openEmptyLayers) && (getRecordset().getRowCount() <= 0)) {
showEmptyLayerMessage();
return false;
}
} catch (ReadDriverException e) {
logger.error(e.getStackTrace());
return false;
}
if (!initController()) {
return false;
}
initGUI();
initWidgets();
refreshGUI();
super.repaint();
super.setVisible(true);
// setOpenNavTableForm(true);
setFocusCycleRoot(true);
setLayerListeners();
return true;
}
/**
* In NavTable it will get the attribute names from the layer and set it on
* the left column of the table. On AbstractForm it will initialize the
* widget vector from the Abeille file
*/
protected abstract void initWidgets();
protected void initGUI() {
MigLayout thisLayout = new MigLayout("inset 0, align center", "[grow]",
"[][grow][]");
this.setLayout(thisLayout);
this.add(getNorthPanel(), "shrink, wrap, align center");
this.add(getCenterPanel(), "shrink, growx, growy, wrap");
this.add(getSouthPanel(), "shrink, align center");
}
protected boolean initController() {
try {
layerController = new LayerController(this.layer);
layerController.read(getPosition());
} catch (ReadDriverException e) {
logger.error(e.getStackTrace(), e);
return false;
}
return true;
}
public void resetListeners() {
removeLayerListeners();
setLayerListeners();
}
public void reinit() {
resetListeners();
// setOpenNavTableForm(true);
}
protected void setLayerListeners() {
if (layer != null) {
// TODO. Alphanumeric navtable adds the listener to the model
listener = new EditionListener(this, layer);
layer.addLayerListener(listener);
}
navigation.setListeners();
addPositionListener(this);
}
protected void removeLayerListeners() {
if (layer != null) {
layer.removeLayerListener(listener);
}
navigation.removeListeners();
removePositionListener(this);
}
public void showEmptyLayerMessage() {
if ((!openEmptyLayers)) {
JOptionPane.showMessageDialog(this,
PluginServices.getText(this, "emptyLayer"));
}
}
/**
* It shows the values of a data row in the main panel.
*/
public abstract void fillValues();
/**
* It fills NavTable with empty values. Used when "Selected" option is set
* on, but there are any selection registers.
*
*/
public abstract void fillEmptyValues();
/**
* It selects a specific row into the table.
*/
public abstract void selectRow(int row);
/**
* @return true is some value has changed, false otherwise
*/
protected boolean isChangedValues() {
return changedValues;
}
/**
* Set true or false the boolean variable changedValues
*/
protected void setChangedValues(boolean bool) {
undoB.setEnabled(bool);
changedValues = bool;
}
public abstract boolean saveRecord() throws StopWriterVisitorException;
protected void enableSaveButton(boolean bool) {
if (!isChangedValues()) {
saveB.setEnabled(false);
} else {
saveB.setEnabled(bool);
}
}
public void setOnlySelected(boolean bool) {
navigation.setOnlySelected(bool);
}
public boolean isOnlySelected() {
return navigation.isOnlySelected();
}
private void initNorthPanelButtons() {
// alwaysSelectCB and onlySelectedCB are init in SelectionHandler
alwaysZoomCB = getNavTableCheckBox(alwaysZoomCB, "alwaysZoomCheckBox");
fixScaleCB = getNavTableCheckBox(fixScaleCB, "fixedScaleCheckBox");
}
private JPanel getOptionsPanel() {
if (optionsPanel == null) {
optionsPanel = new JPanel(new FlowLayout());
optionsPanel.add(navigation.getOptionsPanel());
optionsPanel.add(alwaysZoomCB);
optionsPanel.add(fixScaleCB);
}
return optionsPanel;
}
/**
* Gets the file of the image header for the upper panel. Subclasses can
* override this method to get their custom image header.
*
* @return the File of the image.
*/
protected File getHeaderFile() {
File folder = PluginServices.getPluginServices(this)
.getPluginDirectory();
File header = new File(folder.getAbsolutePath() + File.separator
+ "images" + File.separator + "navtable_header.png");
if (!header.exists()) {
header = new File(Preferences.getConfigDir()
+ "/navtable_header.png");
}
return header;
}
protected JPanel getNorthPanel() {
if (northPanel == null) {
initNorthPanelButtons();
northPanel = new JPanel(new BorderLayout());
File iconFile = getHeaderFile();
if (iconFile != null && iconFile.exists()) {
northPanel.setBackground(Color.WHITE);
ImageIcon logo = new ImageIcon(iconFile.getAbsolutePath());
JLabel icon = new JLabel();
icon.setIcon(logo);
northPanel.add(icon, BorderLayout.WEST);
}
northPanel.add(getOptionsPanel(), BorderLayout.SOUTH);
}
return northPanel;
}
public abstract JPanel getCenterPanel();
public ImageIcon getIcon(String iconName) {
java.net.URL imgURL = getClass().getResource(iconName);
if (imgURL == null) {
imgURL = AbstractNavTable.class.getResource(iconName);
}
ImageIcon icon = new ImageIcon(imgURL);
return icon;
}
// Probably should be removed and use a factory instead
// is duplicated with NavigationHandler
private JButton getNavTableButton(JButton button, String iconName,
String toolTipName) {
JButton but = new JButton(getIcon(iconName));
but.setToolTipText(PluginServices.getText(this, toolTipName));
but.addActionListener(this);
return but;
}
// Probably should be removed and use a factory instead
// is duplicated with NavigationHandler
private JCheckBox getNavTableCheckBox(JCheckBox cb, String toolTipName) {
cb = new JCheckBox(PluginServices.getText(this, toolTipName));
cb.addActionListener(this);
return cb;
}
public JPanel getActionsToolBar() {
if (actionsToolBar == null) {
actionsToolBar = new JPanel(new FlowLayout());
registerNavTableButtonsOnActionToolBarExtensionPoint();
ExtensionPoint actionToolBarEP = (ExtensionPoint) ExtensionPointsSingleton
.getInstance().get(NAVTABLE_ACTIONS_TOOLBAR);
for (Object button : actionToolBarEP.values()) {
actionsToolBar.add((JButton) button);
}
}
return actionsToolBar;
}
protected void registerNavTableButtonsOnActionToolBarExtensionPoint() {
ExtensionPoints extensionPoints = ExtensionPointsSingleton
.getInstance();
filterB = getNavTableButton(filterB, "/filter.png", "filterTooltip");
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-enable-filter",
filterB);
copySelectedB = getNavTableButton(copySelectedB, "/copy-selected.png",
"copySelectedButtonTooltip");
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-copy-selected",
copySelectedB);
copyPreviousB = getNavTableButton(copyPreviousB, "/copy.png",
"copyPreviousButtonTooltip");
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-copy-previous",
copyPreviousB);
zoomB = getNavTableButton(zoomB, "/zoom.png", "zoomButtonTooltip");
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-zoom", zoomB);
// TODO. This is a hack. It's more logic that navigation registers the
// bt
// itself. But as NavTable overrides this methods, and cleans the list
// it can not be done
navigation.registerNavTableButtonsOnActionToolBarExtensionPoint();
saveB = getNavTableButton(saveB, "/save.png", "saveButtonTooltip");
saveB.setEnabled(false);
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-save", saveB);
removeB = getNavTableButton(removeB, "/delete.png", "delete_register");
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-remove", removeB);
undoB = getNavTableButton(undoB, "/edit-undo.png",
"clearChangesButtonTooltip");
undoB.setEnabled(false);
extensionPoints.add(NAVTABLE_ACTIONS_TOOLBAR, "button-clear-changes",
undoB);
}
protected JPanel getSouthPanel() {
if (southPanel == null) {
southPanel = new JPanel(new BorderLayout());
southPanel.add(navigation.getToolBar(), BorderLayout.SOUTH);
southPanel.add(getActionsToolBar(), BorderLayout.NORTH);
}
return southPanel;
}
/**
* Shows a warning to the user if there's unsaved data.
*
*/
protected boolean showWarning() {
if (getPosition() == EMPTY_REGISTER) {
return true;
}
if (isChangedValues()) {
boolean save = false;
Object[] options = {
PluginServices.getText(this, "saveButtonTooltip"),
PluginServices.getText(this, "ignoreButton") };
int response = JOptionPane.showOptionDialog(this,
PluginServices.getText(this, "unsavedDataMessage"),
PluginServices.getText(this, "unsavedDataTitle"),
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
null, // do not use a custom Icon
options, // the titles of buttons
options[0]); // default button title
if (response == JOptionPane.YES_OPTION) {
save = true;
} else {
save = false;
setChangedValues(false);
// The values will be restored as they are
// when filling again the table/form,
// so it's not need to revert the changes done.
}
if (save) {
return tryToSave();
}
}
return true;
}
/**
* Zooms to the current feature. The feature will fill the visualization
* area.
*
*/
public void zoom() {
Rectangle2D rectangle = null;
int pos = Long.valueOf(getPosition()).intValue();
if (layer instanceof AlphanumericData) {
try {
IGeometry g;
ReadableVectorial source = (layer).getSource();
source.start();
g = source.getShape(pos);
source.stop();
if (g != null) {
/*
* fix to avoid zoom problems when layer and view
* projections aren't the same.
*/
if (layer.getCoordTrans() != null) {
g.reProject(layer.getCoordTrans());
}
rectangle = g.getBounds2D();
if (rectangle.getWidth() < 200) {
rectangle.setFrameFromCenter(rectangle.getCenterX(),
rectangle.getCenterY(),
rectangle.getCenterX() + 100,
rectangle.getCenterY() + 100);
}
if (rectangle != null) {
layer.getMapContext().getViewPort()
.setExtent(rectangle);
}
} else {
JOptionPane.showMessageDialog(this, PluginServices.getText(
this, "feature_has_no_geometry_to_zoom"));
}
} catch (InitializeDriverException e) {
logger.error(e.getMessage(), e);
} catch (ReadDriverException e) {
logger.error(e.getMessage(), e);
}
}
}
/**
* It forces the application to use the current scale when navigating
* between features. It also centers the visualization to the feature when
* navigating.
*
*/
private void fixScale() {
long scale = layer.getMapContext().getScaleView();
zoom();
layer.getMapContext().setScaleView(scale);
}
public void selectCurrentFeature() {
FBitSet bitset = null;
int pos = Long.valueOf(getPosition()).intValue();
bitset = getRecordset().getSelection();
if (!bitset.get(pos)) {
bitset.set(pos);
} else {
bitset.clear(pos);
if (isOnlySelected()) {
lastSelected();
}
}
getRecordset().setSelection(bitset);
}
@Deprecated
public void selectFeature(long feature) {
FBitSet bitset = null;
int pos = Long.valueOf(feature).intValue();
bitset = getRecordset().getSelection();
if (!bitset.get(pos)) {
bitset.set(pos);
}
getRecordset().setSelection(bitset);
}
/**
* Removes all selections of the layer.
*
*/
public void clearSelection() {
getRecordset().clearSelection();
}
@Override
public WindowInfo getWindowInfo() {
if (windowInfo == null) {
windowInfo = new WindowInfo(WindowInfo.MODELESSDIALOG
| WindowInfo.PALETTE | WindowInfo.RESIZABLE);
windowInfo.setTitle("NavTable: " + dataName);
Dimension dim = getPreferredSize();
// To calculate the maximum size of a form we take the size of the
// main frame minus a "magic number" for the menus, toolbar, state
// bar
// Take into account that in edition mode there is less available
// space
MDIFrame a = (MDIFrame) PluginServices.getMainFrame();
final int MENU_TOOL_STATE_BAR = 205;
int maxHeight = a.getHeight() - MENU_TOOL_STATE_BAR;
int maxWidth = a.getWidth() - 15;
int width, heigth = 0;
if (dim.getHeight() > maxHeight) {
heigth = maxHeight;
} else {
heigth = new Double(dim.getHeight()).intValue();
}
if (dim.getWidth() > maxWidth) {
width = maxWidth;
} else {
width = new Double(dim.getWidth()).intValue();
}
// getPreferredSize doesn't take into account the borders and other
// stuff
// introduced by Andami, neither scroll bars so we must increase the
// "preferred"
// dimensions
windowInfo.setWidth(width + 25);
windowInfo.setHeight(heigth + 15);
}
return windowInfo;
}
/**
* Repaints the window.
*
*/
public void refreshGUI() {
boolean navEnabled = false;
if (getRecordset() == null) {
return;
}
if (navigation.isEmptyRegister()) {
navEnabled = false;
fillEmptyValues();
} else {
navEnabled = true;
fillValues();
}
// north panel buttons
alwaysZoomCB.setEnabled(navEnabled);
fixScaleCB.setEnabled(navEnabled);
if (isSomeRowToWorkOn()) {
if (alwaysZoomCB.isSelected()) {
zoomB.setEnabled(false);
zoom();
} else {
zoomB.setEnabled(true);
}
if (fixScaleCB.isSelected()) {
fixScale();
}
} else {
fillEmptyValues();
}
// south panel option buttons
enableCopySelectedButton(navEnabled);
enableCopyPreviousButton(navEnabled);
zoomB.setEnabled(navEnabled);
setIconForFiltering();
enableSaveButton(navEnabled);
removeB.setEnabled(navEnabled);
navigation.refreshGUI(navEnabled);
}
private void setIconForFiltering() {
if (getRecordset().getSelection().isEmpty()) {
ImageIcon imagenFilter = getIcon("/filter.png");
filterB.setIcon(imagenFilter);
filterB.setToolTipText(PluginServices
.getText(this, "filterTooltip"));
} else {
ImageIcon imagenRemoveFilter = getIcon("/nofilter.png");
filterB.setIcon(imagenRemoveFilter);
filterB.setToolTipText(PluginServices.getText(this,
"noFilterTooltip"));
}
}
private void enableCopyPreviousButton(boolean navEnabled) {
if (getPosition() == 0 || !navEnabled) {
copyPreviousB.setEnabled(false);
} else {
copyPreviousB.setEnabled(true);
}
}
private void enableCopySelectedButton(boolean navEnabled) {
if (getNumberOfRowsSelected() == 0 || !navEnabled) {
copySelectedB.setEnabled(false);
} else {
copySelectedB.setEnabled(true);
}
}
private boolean isSomeRowToWorkOn() {
if (isOnlySelected() && getNumberOfRowsSelected() == 0) {
return false;
} else {
return true;
}
}
// TODO. Visibility changed after the refactor to implement the sorter
public int getNumberOfRowsSelected() {
FBitSet bitset = getRecordset().getSelection();
return bitset.cardinality();
}
public void copyPrevious() {
long current = navigation.getPosition();
navigation.setPosition(navigation.getPreviousPositionInModel(), false);
fillValues();
navigation.setPosition(current, false);
setChangedValues(true);
enableSaveButton(true);
}
public boolean copySelected() {
if (getNumberOfRowsSelected() != 1) {
// show error
JOptionPane.showMessageDialog(null,
PluginServices.getText(this, "justOneRecordMessage"),
PluginServices.getText(this, "justOneRecordTitle"),
JOptionPane.WARNING_MESSAGE);
return false;
} else {
long current = getPosition();
FBitSet selection = getRecordset().getSelection();
long selectedRow = selection.nextSetBit(0);
navigation.setPosition(selectedRow, false);
fillValues();
navigation.setPosition(current, false);
return true;
}
}
/**
* Handles the user actions.
*/
@Override
public void actionPerformed(ActionEvent e) {
/*
* Variable isSomeNavTableForm open is used as workaround to control
* null pointers exceptions when all forms using navtable are closed
* but, for some strange reason, some of the listeners is still active.
*/
// if (!isSomeNavTableFormOpen()) {
// return;
// }
if (getRecordset() == null) {
// If there is an error on the recordset of the layer
// do nothing.
return;
}
if (e.getSource() == alwaysZoomCB) {
fixScaleCB.setSelected(false);
refreshGUI();
} else if (e.getSource() == fixScaleCB) {
alwaysZoomCB.setSelected(false);
refreshGUI();
} else if (e.getSource() == filterB) {
filterButtonClicked();
} else if (e.getSource() == copySelectedB) {
if (copySelected()) {
setChangedValues(true);
enableSaveButton(true);
}
} else if (e.getSource() == copyPreviousB) {
copyPrevious();
} else if (e.getSource() == zoomB) {
zoom();
} else if (e.getSource() == saveB) {
tryToSave();
} else if (e.getSource() == removeB) {
int answer = JOptionPane.showConfirmDialog(null,
PluginServices.getText(null, deleteMessageKey), null,
JOptionPane.YES_NO_OPTION);
if (answer == 0) {
try {
deleteRecord();
} catch (StopWriterVisitorException ex) {
ex.printStackTrace();
String errorMessage = (ex.getCause() != null) ? ex
.getCause().getMessage() : ex.getMessage(), auxMessage = errorMessage
.replace("ERROR: ", "").replace(" ", "_")
.replace("\n", ""), auxMessageIntl = PluginServices
.getText(this, auxMessage);
if (auxMessageIntl.compareToIgnoreCase(auxMessage) != 0) {
errorMessage = auxMessageIntl;
}
JOptionPane.showMessageDialog(
(Component) PluginServices.getMainFrame(),
errorMessage,
PluginServices.getText(this, saveErrorTitleKey),
JOptionPane.ERROR_MESSAGE);
}
}
} else if (e.getSource() == undoB) {
undoAction();
}
}
private void filterButtonClicked() {
if (getRecordset().getSelection().isEmpty()) {
FiltroExtension fe = new FiltroExtension();
fe.initialize();
fe.setDatasource(getRecordset(), dataName);
fe.execute("FILTER_DATASOURCE");
} else {
clearSelection();
}
}
private boolean tryToSave() {
try {
if (saveRecord()) {
refreshGUI();
} else {
JOptionPane.showMessageDialog(this, PluginServices.getText(
this, saveErrorGenericMessageKey), "",
JOptionPane.ERROR_MESSAGE);
}
} catch (StopWriterVisitorException ex) {
logger.error(ex.getStackTrace(), ex);
String errorMessage = (ex.getCause() != null) ? ex.getCause()
.getMessage() : ex.getMessage(), auxMessage = errorMessage
.replace("ERROR: ", "").replace(" ", "_").replace("\n", ""), auxMessageIntl = PluginServices
.getText(this, auxMessage);
if (auxMessageIntl.compareToIgnoreCase(auxMessage) != 0) {
errorMessage = auxMessageIntl;
}
JOptionPane.showMessageDialog(
(Component) PluginServices.getMainFrame(), errorMessage,
PluginServices.getText(this, saveErrorTitleKey),
JOptionPane.ERROR_MESSAGE);
return false;
}
return true;
}
protected void undoAction() {
fillValues();
setChangedValues(false);
refreshGUI();
}
public void deleteRecord() throws StopWriterVisitorException {
try {
long position = getPosition();
layerController.delete(position);
// keep the current position within boundaries
setPosition(position);
if (layerController.getRowCount() <= 0) {
showEmptyLayerMessage();
}
} catch (StopWriterVisitorException e) {
throw e;
} catch (BaseException e) {
logger.error(e.getMessage(), e.getCause());
}
}
@Override
public void windowClosed() {
showWarning();
removeLayerListeners();
// setOpenNavTableForm(false);
}
// private boolean isSomeNavTableFormOpen() {
// return isSomeNavTableFormOpen;
// }
//
// protected void setOpenNavTableForm(boolean b) {
// isSomeNavTableFormOpen = b;
// }
@Override
public void windowActivated() {
}
/**
* Reloads recordset from layer, if possible.
*
* @throws ReadDriverException
*/
public void reloadRecordset() throws ReadDriverException {
getRecordset().reload();
}
public String getDataName() {
return this.dataName;
}
public void addPositionListener(PositionListener l) {
navigation.addEventListener(l);
}
public void removePositionListener(PositionListener l) {
navigation.removeEventListener(l);
}
public abstract boolean isSavingValues();
public SelectableDataSource getRecordset() {
try {
return layer.getSource().getRecordset();
} catch (ReadDriverException e) {
logger.error(e.getStackTrace(), e);
return null;
}
}
@Override
public void onPositionChange(PositionEvent e) {
try {
layerController.read(getPosition());
refreshGUI();
} catch (ReadDriverException rde) {
logger.error(rde.getStackTrace(), rde);
layerController.clearAll();
refreshGUI();
}
}
@Override
public void beforePositionChange(PositionEvent e) {
showWarning();
// TODO. If showWarning returns false, the record was not correctly
// saved so we should not move to the next position. Or if is a movement
// for other reason like click onlySelected, the click should be avoided
}
public void next() {
navigation.next();
}
public void last() {
navigation.last();
}
private void lastSelected() {
navigation.lastSelected();
}
public void first() {
navigation.first();
}
// TODO. Visibility changed after the refactor to implement the sorter
public void firstSelected() {
navigation.firstSelected();
}
public void before() {
navigation.goToPreviousInView();
}
public void setPosition(long newPosition) {
navigation.setPosition(newPosition);
}
public long getPosition() {
return navigation.getPosition();
}
public void setSortKeys(List<? extends SortKey> keys) {
navigation.setSortKeys(keys);
}
public List<? extends SortKey> getSortKeys() {
return navigation.getSortKeys();
}
/**
* Only process stop edition events. And when this occurs all the sort is
* recalculated. In case that recalculate the full sorting has bad
* performance we should process FIELD_EDITION, ROW_EDITION EditionEvents,
* and reorder only the affected rows, or test the performance of not create
* a new instance of the sorter.
*/
public void layerEvent(LayerEvent e) {
if ((e.getEventType() == LayerEvent.EDITION_CHANGED)
&& !layer.isEditing()) {
refresh();
navigation.modelChanged();
}
}
public void editionEvent(EditionEvent e) {
if ((e instanceof AfterFieldEditEvent)
&& (e.getChangeType() == EditionEvent.CHANGE_TYPE_DELETE)) {
refresh();
navigation.setSortKeys(null);
}
}
private void refresh() {
try {
reloadRecordset();
} catch (ReadDriverException error) {
logger.error(error.getMessage(), error);
}
refreshGUI();
}
}