001package jmri.jmrix.rps.aligntable;
002
003import java.awt.Dimension;
004import java.awt.FlowLayout;
005import java.awt.event.ActionEvent;
006import java.awt.event.ActionListener;
007import java.beans.PropertyChangeListener;
008import java.util.ResourceBundle;
009import javax.swing.BoxLayout;
010import javax.swing.JButton;
011import javax.swing.JComboBox;
012import javax.swing.JLabel;
013import javax.swing.JPanel;
014import javax.swing.JScrollPane;
015import javax.swing.JTable;
016import javax.swing.JTextField;
017import javax.swing.SortOrder;
018import javax.swing.table.AbstractTableModel;
019import javax.swing.table.TableCellEditor;
020import javax.swing.table.TableRowSorter;
021import javax.vecmath.Point3d;
022import jmri.jmrix.rps.Algorithms;
023import jmri.jmrix.rps.Engine;
024import jmri.jmrix.rps.Receiver;
025import jmri.swing.RowSorterUtil;
026import jmri.util.table.ButtonEditor;
027import jmri.util.table.ButtonRenderer;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * Pane for user management of RPS alignment.
033 *
034 * @author Bob Jacobsen Copyright (C) 2008
035 */
036public class AlignTablePane extends javax.swing.JPanel {
037
038    static ResourceBundle rb = ResourceBundle.getBundle("jmri.jmrix.rps.aligntable.AlignTableBundle");
039
040    /**
041     * Constructor method.
042     * @param flag the ModifiedFlag tag.
043     */
044    public AlignTablePane(jmri.ModifiedFlag flag) {
045        super();
046        this.flag = flag;
047    }
048
049    AlignModel alignModel = null;
050    jmri.ModifiedFlag flag;
051
052    /**
053     * Initialize the window
054     */
055    public void initComponents() {
056
057        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
058
059        alignModel = new AlignModel();
060
061        JTable alignTable = new JTable(alignModel);
062
063        // install a button renderer & editor
064        ButtonRenderer buttonRenderer = new ButtonRenderer();
065        alignTable.setDefaultRenderer(JButton.class, buttonRenderer);
066        TableCellEditor buttonEditor = new ButtonEditor(new JButton());
067        alignTable.setDefaultEditor(JButton.class, buttonEditor);
068
069        TableRowSorter<AlignModel> sorter = new TableRowSorter<>(alignModel);
070        RowSorterUtil.setSortOrder(sorter, AlignModel.NUMCOL, SortOrder.ASCENDING);
071        alignTable.setRowSelectionAllowed(false);
072        alignTable.setPreferredScrollableViewportSize(new java.awt.Dimension(580, 80));
073
074        JScrollPane scrollPane = new JScrollPane(alignTable);
075        add(scrollPane);
076
077        // status info on bottom
078        JPanel p = new JPanel() {
079
080            @Override
081            public Dimension getMaximumSize() {
082                int height = getPreferredSize().height;
083                int width = super.getMaximumSize().width;
084                return new Dimension(width, height);
085            }
086        };
087        p.setLayout(new FlowLayout());
088
089        p.add(new JLabel(rb.getString("LabelNumCol")));
090        num.setText("" + Engine.instance().getMaxReceiverNumber());
091        p.add(num);
092
093        JButton b = new JButton(rb.getString("ButtonSet"));
094        b.addActionListener(new ActionListener() {
095            @Override
096            public void actionPerformed(ActionEvent event) {
097                // set number of columns
098                Engine.instance().setMaxReceiverNumber(
099                        Integer.parseInt(num.getText()));
100                // mark modification
101                flag.setModifiedFlag(true);
102                // resize table
103                alignModel.fireTableStructureChanged();
104
105            }
106        });
107        p.add(b);
108        add(p);
109
110        p = new JPanel() {
111
112            @Override
113            public Dimension getMaximumSize() {
114                int height = getPreferredSize().height;
115                int width = super.getMaximumSize().width;
116                return new Dimension(width, height);
117            }
118        };
119        p.setLayout(new FlowLayout());
120
121        p.add(new JLabel(rb.getString("LabelVSound")));
122        vsound.setText("" + Engine.instance().getVSound());
123        p.add(vsound);
124
125        p.add(new JLabel(rb.getString("LabelOffset")));
126        offset.setText("" + Engine.instance().getOffset());
127        p.add(offset);
128
129        p.add(new JLabel(rb.getString("LabelAlgorithm")));
130        p.add(algorithmBox);
131
132        b = new JButton(rb.getString("ButtonSet"));
133        b.addActionListener(new ActionListener() {
134            @Override
135            public void actionPerformed(ActionEvent event) {
136                // set number of vsound, offset
137                Engine.instance().setOffset(
138                        Integer.parseInt(offset.getText()));
139                Engine.instance().setVSound(
140                        Double.parseDouble(vsound.getText()));
141                Engine.instance().setAlgorithm(
142                        (String) algorithmBox.getSelectedItem());
143                // mark modification
144                flag.setModifiedFlag(true);
145            }
146        });
147        p.add(b);
148        add(p);
149
150        //
151        add(loadStore = new jmri.jmrix.rps.swing.LoadStorePanel() {
152            // make sure we redisplay if changed
153            @Override
154            public void load() {
155                super.load();
156                alignModel.fireTableStructureChanged();
157                // modified (to force store of default after load new values)
158                flag.setModifiedFlag(true);
159            }
160
161            @Override
162            public void storeDefault() {
163                super.storeDefault();
164                // no longer modified after storeDefault
165                flag.setModifiedFlag(false);
166            }
167        });
168
169        // add sound listener
170        Engine.instance().addPropertyChangeListener(new PropertyChangeListener() {
171            @Override
172            public void propertyChange(java.beans.PropertyChangeEvent e) {
173                if (e.getPropertyName().equals("vSound")) {
174                    // update sound display
175                    vsound.setText("" + e.getNewValue());
176                }
177            }
178        });
179    }
180
181    jmri.jmrix.rps.swing.LoadStorePanel loadStore;
182
183    void storeDefault() {
184        loadStore.storeDefault();
185        // no longer modified after storeDefault
186        flag.setModifiedFlag(false);
187    }
188
189    JTextField num = new JTextField(4);
190    JTextField vsound = new JTextField(8);
191    JTextField offset = new JTextField(4);
192    JComboBox<String> algorithmBox = Algorithms.algorithmBox();
193
194    /**
195     * Set up table for showing individual receivers
196     * <ol>
197     * <li>Address
198     * <li>Present Y/N
199     * <li>Edit button
200     * </ol>
201     */
202    public class AlignModel extends AbstractTableModel {
203
204        static private final int NUMCOL = 0;
205        static private final int XCOL = 1;
206        static private final int YCOL = 2;
207        static private final int ZCOL = 3;
208
209        static private final int LASTTIMECOL = 4;
210
211        static private final int ACTIVECOL = 5;
212
213        static private final int MINTIMECOL = 6;
214        static private final int MAXTIMECOL = 7;
215
216        static private final int LAST = MAXTIMECOL;
217
218        @Override
219        public int getColumnCount() {
220            return LAST + 1;
221        }
222
223        @Override
224        public int getRowCount() {
225            return Engine.instance().getMaxReceiverNumber();
226        }
227
228        @Override
229        public String getColumnName(int c) {
230            switch (c) {
231                case NUMCOL:
232                    return rb.getString("TitleColNum");
233                case XCOL:
234                    return rb.getString("TitleColX");
235                case YCOL:
236                    return rb.getString("TitleColY");
237                case ZCOL:
238                    return rb.getString("TitleColZ");
239                case LASTTIMECOL:
240                    return rb.getString("TitleColLast");
241                case ACTIVECOL:
242                    return rb.getString("TitleColActive");
243                case MINTIMECOL:
244                    return rb.getString("TitleColMin");
245                case MAXTIMECOL:
246                    return rb.getString("TitleColMax");
247                default:
248                    return "";
249            }
250        }
251
252        @Override
253        public Class<?> getColumnClass(int c) {
254            if (c == XCOL || c == YCOL || c == ZCOL) {
255                return Double.class;
256            } else if (c == NUMCOL) {
257                return Integer.class;
258            } else if (c == ACTIVECOL) {
259                return Boolean.class;
260            }
261            if (c == MINTIMECOL || c == MAXTIMECOL || c == LASTTIMECOL) {
262                return Integer.class;
263            } else {
264                return String.class;
265            }
266        }
267
268        @Override
269        public boolean isCellEditable(int r, int c) {
270            if (c == XCOL || c == YCOL || c == ZCOL || c == ACTIVECOL
271                    || c == MINTIMECOL || c == MAXTIMECOL) {
272                return true;
273            } else {
274                return false;
275            }
276        }
277
278        @Override
279        public Object getValueAt(int r, int c) {
280            // r is row number, from 0; receiver addresses start at 1
281            Receiver rc;
282            switch (c) {
283                case NUMCOL:
284                    return Integer.valueOf(r + 1);
285                case XCOL:
286                    rc = Engine.instance().getReceiver(r + 1);
287                    if (rc == null) {
288                        return null;
289                    }
290                    return Double.valueOf(rc.getPosition().x);
291                case YCOL:
292                    rc = Engine.instance().getReceiver(r + 1);
293                    if (rc == null) {
294                        return null;
295                    }
296                    return Double.valueOf(rc.getPosition().y);
297                case ZCOL:
298                    rc = Engine.instance().getReceiver(r + 1);
299                    if (rc == null) {
300                        return null;
301                    }
302                    return Double.valueOf(rc.getPosition().z);
303                case ACTIVECOL:
304                    rc = Engine.instance().getReceiver(r + 1);
305                    if (rc == null) {
306                        return null;
307                    }
308                    return Boolean.valueOf(rc.isActive());
309                case LASTTIMECOL:
310                    rc = Engine.instance().getReceiver(r + 1);
311                    if (rc == null) {
312                        return null;
313                    }
314                    return Integer.valueOf(rc.getLastTime());
315                case MINTIMECOL:
316                    rc = Engine.instance().getReceiver(r + 1);
317                    if (rc == null) {
318                        return null;
319                    }
320                    return Integer.valueOf(rc.getMinTime());
321                case MAXTIMECOL:
322                    rc = Engine.instance().getReceiver(r + 1);
323                    if (rc == null) {
324                        return null;
325                    }
326                    return Integer.valueOf(rc.getMaxTime());
327                default:
328                    return null;
329            }
330        }
331
332        @Override
333        public void setValueAt(Object val, int r, int c) {
334            // r is row number, from 0
335            Receiver rc;
336            Point3d p;
337            switch (c) {
338                case XCOL:
339                    rc = Engine.instance().getReceiver(r + 1);
340                    if (rc == null) {
341                        rc = new Receiver(new Point3d(0., 0., 0.));
342                        Engine.instance().setReceiver(r + 1, rc);
343                    }
344                    p = rc.getPosition();
345                    p.x = ((Double) val).doubleValue();
346                    Engine.instance().setReceiverPosition(r + 1, p);
347                    flag.setModifiedFlag(true);
348                    break;
349                case YCOL:
350                    rc = Engine.instance().getReceiver(r + 1);
351                    if (rc == null) {
352                        rc = new Receiver(new Point3d(0., 0., 0.));
353                        Engine.instance().setReceiver(r + 1, rc);
354                    }
355                    p = rc.getPosition();
356                    p.y = ((Double) val).doubleValue();
357                    Engine.instance().setReceiverPosition(r + 1, p);
358                    flag.setModifiedFlag(true);
359                    break;
360                case ZCOL:
361                    rc = Engine.instance().getReceiver(r + 1);
362                    if (rc == null) {
363                        rc = new Receiver(new Point3d(0., 0., 0.));
364                        Engine.instance().setReceiver(r + 1, rc);
365                    }
366                    p = rc.getPosition();
367                    p.z = ((Double) val).doubleValue();
368                    Engine.instance().setReceiverPosition(r + 1, p);
369                    flag.setModifiedFlag(true);
370                    break;
371                case ACTIVECOL:
372                    rc = Engine.instance().getReceiver(r + 1);
373                    if (rc == null) {
374                        rc = new Receiver(new Point3d(0., 0., 0.));
375                        Engine.instance().setReceiver(r + 1, rc);
376                    }
377                    rc.setActive(((Boolean) val).equals(Boolean.TRUE));
378                    flag.setModifiedFlag(true);
379                    break;
380                case MINTIMECOL:
381                    rc = Engine.instance().getReceiver(r + 1);
382                    if (rc == null) {
383                        rc = new Receiver(new Point3d(0., 0., 0.));
384                        Engine.instance().setReceiver(r + 1, rc);
385                    }
386                    int min = ((Integer) val).intValue();
387                    Engine.instance().getReceiver(r + 1).setMinTime(min);
388                    flag.setModifiedFlag(true);
389                    break;
390                case MAXTIMECOL:
391                    rc = Engine.instance().getReceiver(r + 1);
392                    if (rc == null) {
393                        rc = new Receiver(new Point3d(0., 0., 0.));
394                        Engine.instance().setReceiver(r + 1, rc);
395                    }
396                    int max = ((Integer) val).intValue();
397                    Engine.instance().getReceiver(r + 1).setMaxTime(max);
398                    flag.setModifiedFlag(true);
399                    break;
400                default:
401                    log.error("setValueAt of column {}", c);
402            }
403        }
404    }
405
406    private final static Logger log = LoggerFactory.getLogger(AlignTablePane.class);
407
408}