package tim.prune.function.edit;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellRenderer;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.config.Config;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.FieldList;
import tim.prune.data.Track;
import tim.prune.data.Unit;
/**
* Class to manage the display and editing of point data
*/
public class PointEditor
{
private App _app = null;
private JFrame _parentFrame = null;
private JDialog _dialog = null;
private JTable _table = null;
private JLabel _fieldnameLabel = null;
private JTextField _valueField = null;
private JTextArea _valueArea = null;
private JScrollPane _valueAreaPane = null;
private Track _track = null;
private DataPoint _point = null;
private EditFieldsTableModel _model = null;
private int _prevRowIndex = -1;
/**
* Constructor
* @param inApp application object to inform of success
* @param inParentFrame parent frame
*/
public PointEditor(App inApp, JFrame inParentFrame)
{
_app = inApp;
_parentFrame = inParentFrame;
}
/**
* Show the edit point dialog
* @param inTrack track object
* @param inPoint point to edit
*/
public void showDialog(Track inTrack, DataPoint inPoint)
{
_track = inTrack;
_point = inPoint;
_dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.pointedit.title"), true);
_dialog.setLocationRelativeTo(_parentFrame);
// Check field list
FieldList fieldList = _track.getFieldList();
int numFields = fieldList.getNumFields();
// Create table model for point editor
_model = new EditFieldsTableModel(numFields);
for (int i=0; i<numFields; i++)
{
Field field = fieldList.getField(i);
_model.addFieldInfo(field.getName(), _point.getFieldValue(field), i);
}
// Create Gui
_dialog.getContentPane().add(makeDialogComponents());
_dialog.pack();
// Init right-hand side
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_valueField.setVisible(false);
_valueAreaPane.setVisible(false);
}
});
_dialog.setVisible(true);
}
/**
* Make the dialog components
* @return the GUI components for the dialog
*/
private Component makeDialogComponents()
{
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout(20, 10));
// Create GUI layout for point editor
_table = new JTable(_model)
{
// Paint the changed fields orange
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component comp = super.prepareRenderer(renderer, row, column);
boolean changed = ((EditFieldsTableModel) getModel()).getChanged(row);
comp.setBackground(changed ? Color.orange : getBackground());
return comp;
}
};
_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
_table.getSelectionModel().clearSelection();
_table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
fieldSelected();
}
});
_table.setPreferredScrollableViewportSize(new Dimension(_table.getWidth() * 2, _table.getRowHeight() * 6));
JScrollPane tablePane = new JScrollPane(_table);
tablePane.setPreferredSize(new Dimension(150, 100));
// Label at top
JLabel topLabel = new JLabel(I18nManager.getText("dialog.pointedit.intro"));
topLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 3, 6));
panel.add(topLabel, BorderLayout.NORTH);
// listener for ok event
ActionListener okListener = new ActionListener() {
public void actionPerformed(ActionEvent e)
{
// update App with edit
confirmEdit();
_dialog.dispose();
}
};
JPanel rightPanel = new JPanel();
rightPanel.setLayout(new BorderLayout());
JPanel rightiPanel = new JPanel();
rightiPanel.setLayout(new BoxLayout(rightiPanel, BoxLayout.Y_AXIS));
// Add GUI elements to rhs
_fieldnameLabel = new JLabel(I18nManager.getText("dialog.pointedit.nofield"));
rightiPanel.add(_fieldnameLabel);
_valueField = new JTextField(11);
// Add listener for enter button
_valueField.addActionListener(okListener);
rightiPanel.add(_valueField);
rightPanel.add(rightiPanel, BorderLayout.NORTH);
_valueArea = new JTextArea(5, 15);
_valueArea.setLineWrap(true);
_valueArea.setWrapStyleWord(true);
_valueAreaPane = new JScrollPane(_valueArea);
rightPanel.add(_valueAreaPane, BorderLayout.CENTER);
// Put the table and the right-hand panel together in a grid
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(0, 2, 10, 10));
mainPanel.add(tablePane);
mainPanel.add(rightPanel);
panel.add(mainPanel, BorderLayout.CENTER);
// Bottom panel for OK, cancel buttons
JPanel lowerPanel = new JPanel();
lowerPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
_dialog.dispose();
}
});
lowerPanel.add(cancelButton);
JButton okButton = new JButton(I18nManager.getText("button.ok"));
okButton.addActionListener(okListener);
lowerPanel.add(okButton);
panel.add(lowerPanel, BorderLayout.SOUTH);
return panel;
}
/**
* When table selection changes, need to update model and go to the selected field
*/
private void fieldSelected()
{
int rowNum = _table.getSelectedRow();
if (rowNum == _prevRowIndex) {return;} // selection hasn't changed
// Check the current values
if (_prevRowIndex >= 0)
{
Field prevField = _track.getFieldList().getField(_prevRowIndex);
boolean littleField = prevField.isBuiltIn() && prevField != Field.DESCRIPTION;
String newValue = littleField ? _valueField.getText() : _valueArea.getText();
// Update the model from the current GUI values
_model.updateValue(_prevRowIndex, newValue);
}
if (rowNum < 0)
{
_fieldnameLabel.setText("");
}
else
{
String currValue = _model.getValue(rowNum);
Field field = _track.getFieldList().getField(rowNum);
_fieldnameLabel.setText(makeFieldLabel(field, _point));
_fieldnameLabel.setVisible(true);
boolean littleField = field.isBuiltIn() && field != Field.DESCRIPTION;
if (littleField) {
_valueField.setText(currValue);
}
else {
_valueArea.setText(currValue);
}
_valueField.setVisible(littleField);
_valueAreaPane.setVisible(!littleField);
if (littleField) {
_valueField.requestFocus();
}
else {
_valueArea.requestFocus();
}
}
_prevRowIndex = rowNum;
}
/**
* @param inField field
* @param inPoint current point
* @return label string for above the entry field / area
*/
private static String makeFieldLabel(Field inField, DataPoint inPoint)
{
String label = I18nManager.getText("dialog.pointedit.table.field") + ": " + inField.getName();
// Add units if the field is altitude / speed / vspeed
if (inField == Field.ALTITUDE)
{
label += makeUnitsLabel(inPoint.hasAltitude() ? inPoint.getAltitude().getUnit() : Config.getUnitSet().getAltitudeUnit());
}
else if (inField == Field.SPEED)
{
label += makeUnitsLabel(inPoint.hasHSpeed() ? inPoint.getHSpeed().getUnit() : Config.getUnitSet().getSpeedUnit());
}
else if (inField == Field.VERTICAL_SPEED)
{
label += makeUnitsLabel(inPoint.hasVSpeed() ? inPoint.getVSpeed().getUnit() : Config.getUnitSet().getVerticalSpeedUnit());
}
return label;
}
/**
* @param inUnit units for altitude / speed
* @return addition to the field label to describe the units
*/
private static String makeUnitsLabel(Unit inUnit)
{
if (inUnit == null) return "";
return " (" + I18nManager.getText(inUnit.getShortnameKey()) + ")";
}
/**
* Confirm the edit and inform the app
*/
private void confirmEdit()
{
// Apply the edits to the current field
int rowNum = _table.getSelectedRow();
if (rowNum >= 0)
{
Field currField = _track.getFieldList().getField(rowNum);
boolean littleField = currField.isBuiltIn() && currField != Field.DESCRIPTION;
String newValue = littleField ? _valueField.getText() : _valueArea.getText();
_model.updateValue(_prevRowIndex, newValue);
}
// Package the modified fields into an object
FieldList fieldList = _track.getFieldList();
int numFields = fieldList.getNumFields();
// Make lists for edit and undo, and add each changed field in turn
FieldEditList editList = new FieldEditList();
FieldEditList undoList = new FieldEditList();
for (int i=0; i<numFields; i++)
{
if (_model.getChanged(i))
{
Field field = fieldList.getField(i);
editList.addEdit(new FieldEdit(field, _model.getValue(i)));
undoList.addEdit(new FieldEdit(field, _point.getFieldValue(field)));
}
}
_app.completePointEdit(editList, undoList);
}
}