1 package tim.prune.function.edit;
3 import java.awt.BorderLayout;
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.FlowLayout;
8 import java.awt.GridLayout;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.awt.event.KeyAdapter;
12 import java.awt.event.KeyEvent;
14 import javax.swing.BorderFactory;
15 import javax.swing.BoxLayout;
16 import javax.swing.JButton;
17 import javax.swing.JDialog;
18 import javax.swing.JFrame;
19 import javax.swing.JLabel;
20 import javax.swing.JPanel;
21 import javax.swing.JScrollPane;
22 import javax.swing.JTable;
23 import javax.swing.JTextArea;
24 import javax.swing.JTextField;
25 import javax.swing.ListSelectionModel;
26 import javax.swing.SwingUtilities;
27 import javax.swing.event.ListSelectionEvent;
28 import javax.swing.event.ListSelectionListener;
29 import javax.swing.table.TableCellRenderer;
32 import tim.prune.I18nManager;
33 import tim.prune.config.Config;
34 import tim.prune.data.DataPoint;
35 import tim.prune.data.Field;
36 import tim.prune.data.FieldList;
37 import tim.prune.data.Track;
38 import tim.prune.data.Unit;
41 * Class to manage the display and editing of point data
43 public class PointEditor
45 private App _app = null;
46 private JFrame _parentFrame = null;
47 private JDialog _dialog = null;
48 private JTable _table = null;
49 private JLabel _fieldnameLabel = null;
50 private JTextField _valueField = null;
51 private JTextArea _valueArea = null;
52 private JScrollPane _valueAreaPane = null;
53 private Track _track = null;
54 private DataPoint _point = null;
55 private EditFieldsTableModel _model = null;
56 private JButton _cancelButton = null;
57 private int _prevRowIndex = -1;
62 * @param inApp application object to inform of success
63 * @param inParentFrame parent frame
65 public PointEditor(App inApp, JFrame inParentFrame)
68 _parentFrame = inParentFrame;
73 * Show the edit point dialog
74 * @param inTrack track object
75 * @param inPoint point to edit
77 public void showDialog(Track inTrack, DataPoint inPoint)
81 _dialog = new JDialog(_parentFrame, I18nManager.getText("dialog.pointedit.title"), true);
82 _dialog.setLocationRelativeTo(_parentFrame);
84 FieldList fieldList = _track.getFieldList();
85 int numFields = fieldList.getNumFields();
86 // Create table model for point editor
87 _model = new EditFieldsTableModel(numFields);
88 for (int i=0; i<numFields; i++)
90 Field field = fieldList.getField(i);
91 _model.addFieldInfo(field.getName(), _point.getFieldValue(field), i);
94 _dialog.getContentPane().add(makeDialogComponents());
96 // Init right-hand side
97 SwingUtilities.invokeLater(new Runnable() {
99 _valueField.setVisible(false);
100 _valueAreaPane.setVisible(false);
101 _cancelButton.requestFocus();
104 _dialog.setVisible(true);
109 * Make the dialog components
110 * @return the GUI components for the dialog
112 private Component makeDialogComponents()
114 JPanel panel = new JPanel();
115 panel.setLayout(new BorderLayout(20, 10));
116 // Create GUI layout for point editor
117 _table = new JTable(_model)
119 // Paint the changed fields orange
120 public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
122 Component comp = super.prepareRenderer(renderer, row, column);
123 boolean changed = ((EditFieldsTableModel) getModel()).getChanged(row);
124 comp.setBackground(changed ? Color.orange : getBackground());
128 _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
129 _table.getSelectionModel().clearSelection();
130 _table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
131 public void valueChanged(ListSelectionEvent e)
136 _table.setPreferredScrollableViewportSize(new Dimension(_table.getWidth() * 2, _table.getRowHeight() * 6));
137 JScrollPane tablePane = new JScrollPane(_table);
138 tablePane.setPreferredSize(new Dimension(150, 100));
141 JLabel topLabel = new JLabel(I18nManager.getText("dialog.pointedit.intro"));
142 topLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 3, 6));
143 panel.add(topLabel, BorderLayout.NORTH);
145 // listener for ok event
146 ActionListener okListener = new ActionListener() {
147 public void actionPerformed(ActionEvent e)
149 // update App with edit
155 JPanel rightPanel = new JPanel();
156 rightPanel.setLayout(new BorderLayout());
157 JPanel rightiPanel = new JPanel();
158 rightiPanel.setLayout(new BoxLayout(rightiPanel, BoxLayout.Y_AXIS));
159 // Add GUI elements to rhs
160 _fieldnameLabel = new JLabel(I18nManager.getText("dialog.pointedit.nofield"));
161 rightiPanel.add(_fieldnameLabel);
162 _valueField = new JTextField(11);
163 // Add listener for enter button
164 _valueField.addActionListener(okListener);
165 rightiPanel.add(_valueField);
166 rightPanel.add(rightiPanel, BorderLayout.NORTH);
167 _valueArea = new JTextArea(5, 15);
168 _valueArea.setLineWrap(true);
169 _valueArea.setWrapStyleWord(true);
170 _valueAreaPane = new JScrollPane(_valueArea);
171 rightPanel.add(_valueAreaPane, BorderLayout.CENTER);
173 // Put the table and the right-hand panel together in a grid
174 JPanel mainPanel = new JPanel();
175 mainPanel.setLayout(new GridLayout(0, 2, 10, 10));
176 mainPanel.add(tablePane);
177 mainPanel.add(rightPanel);
178 panel.add(mainPanel, BorderLayout.CENTER);
180 // Bottom panel for OK, cancel buttons
181 JPanel lowerPanel = new JPanel();
182 lowerPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
183 _cancelButton = new JButton(I18nManager.getText("button.cancel"));
184 _cancelButton.addActionListener(new ActionListener() {
185 public void actionPerformed(ActionEvent e)
190 _cancelButton.addKeyListener(new KeyAdapter() {
191 public void keyPressed(KeyEvent inE) {
192 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
195 lowerPanel.add(_cancelButton);
196 JButton okButton = new JButton(I18nManager.getText("button.ok"));
197 okButton.addActionListener(okListener);
198 lowerPanel.add(okButton);
199 panel.add(lowerPanel, BorderLayout.SOUTH);
205 * When table selection changes, need to update model and go to the selected field
207 private void fieldSelected()
209 int rowNum = _table.getSelectedRow();
210 if (rowNum == _prevRowIndex) {return;} // selection hasn't changed
211 // Check the current values
212 if (_prevRowIndex >= 0)
214 Field prevField = _track.getFieldList().getField(_prevRowIndex);
215 boolean littleField = prevField.isBuiltIn() && prevField != Field.DESCRIPTION;
216 String newValue = littleField ? _valueField.getText() : _valueArea.getText();
217 // Update the model from the current GUI values
218 _model.updateValue(_prevRowIndex, newValue);
223 _fieldnameLabel.setText("");
227 String currValue = _model.getValue(rowNum);
228 Field field = _track.getFieldList().getField(rowNum);
229 _fieldnameLabel.setText(makeFieldLabel(field, _point));
230 _fieldnameLabel.setVisible(true);
231 boolean littleField = field.isBuiltIn() && field != Field.DESCRIPTION;
233 _valueField.setText(currValue);
236 _valueArea.setText(currValue);
238 _valueField.setVisible(littleField);
239 _valueAreaPane.setVisible(!littleField);
241 _valueField.requestFocus();
244 _valueArea.requestFocus();
247 _prevRowIndex = rowNum;
251 * @param inField field
252 * @param inPoint current point
253 * @return label string for above the entry field / area
255 private static String makeFieldLabel(Field inField, DataPoint inPoint)
257 String label = I18nManager.getText("dialog.pointedit.table.field") + ": " + inField.getName();
258 // Add units if the field is altitude / speed / vspeed
259 if (inField == Field.ALTITUDE)
261 label += makeUnitsLabel(inPoint.hasAltitude() ? inPoint.getAltitude().getUnit() : Config.getUnitSet().getAltitudeUnit());
263 else if (inField == Field.SPEED)
265 label += makeUnitsLabel(inPoint.hasHSpeed() ? inPoint.getHSpeed().getUnit() : Config.getUnitSet().getSpeedUnit());
267 else if (inField == Field.VERTICAL_SPEED)
269 label += makeUnitsLabel(inPoint.hasVSpeed() ? inPoint.getVSpeed().getUnit() : Config.getUnitSet().getVerticalSpeedUnit());
275 * @param inUnit units for altitude / speed
276 * @return addition to the field label to describe the units
278 private static String makeUnitsLabel(Unit inUnit)
280 if (inUnit == null) return "";
281 return " (" + I18nManager.getText(inUnit.getShortnameKey()) + ")";
285 * Confirm the edit and inform the app
287 private void confirmEdit()
289 // Apply the edits to the current field
290 int rowNum = _table.getSelectedRow();
293 Field currField = _track.getFieldList().getField(rowNum);
294 boolean littleField = currField.isBuiltIn() && currField != Field.DESCRIPTION;
295 String newValue = littleField ? _valueField.getText() : _valueArea.getText();
296 _model.updateValue(_prevRowIndex, newValue);
299 // Package the modified fields into an object
300 FieldList fieldList = _track.getFieldList();
301 int numFields = fieldList.getNumFields();
302 // Make lists for edit and undo, and add each changed field in turn
303 FieldEditList editList = new FieldEditList();
304 FieldEditList undoList = new FieldEditList();
305 for (int i=0; i<numFields; i++)
307 if (_model.getChanged(i))
309 Field field = fieldList.getField(i);
310 if (field == field.WAYPT_NAME) {
311 if (wasNameAdded(_model.getValue(i))) {
312 _app.createPoint(_point.clonePoint());
315 editList.addEdit(new FieldEdit(field, _model.getValue(i)));
316 undoList.addEdit(new FieldEdit(field, _point.getFieldValue(field)));
319 _app.completePointEdit(editList, undoList);
322 private boolean wasNameAdded(String newName)
324 String prevName = _point.getWaypointName();
325 boolean prevNull = (prevName == null || prevName.equals(""));
326 boolean newNull = (newName == null || newName.equals(""));
327 return (prevNull && !newNull);