001package jmri.jmrix.tams.swing.locodatabase;
002
003import java.util.ArrayList;
004
005import javax.swing.JButton;
006import javax.swing.JLabel;
007import javax.swing.JTable;
008import javax.swing.JTextField;
009import javax.swing.table.TableCellEditor;
010import javax.swing.table.TableColumnModel;
011
012import jmri.jmrix.tams.TamsListener;
013import jmri.jmrix.tams.TamsMessage;
014import jmri.jmrix.tams.TamsReply;
015import jmri.util.swing.JmriMouseEvent;
016import jmri.util.table.ButtonEditor;
017import jmri.util.table.ButtonRenderer;
018
019import org.slf4j.Logger;
020import org.slf4j.LoggerFactory;
021
022/**
023 * Table data model for display the loco database of the Tams MC.
024 *
025 * @author Kevin Dickerson Copyright (C) 2012
026 */
027public class LocoDataModel extends javax.swing.table.AbstractTableModel implements TamsListener {
028
029    static public final int ADDRCOLUMN = 0;
030    static public final int SPDCOLUMN = 1;
031    static public final int FMTCOLUMN = 2;
032    static public final int NAMECOLUMN = 3;
033    static public final int DELCOLUMN = 4;
034
035    static public final int NUMCOLUMN = 5;
036
037    jmri.jmrix.tams.TamsSystemConnectionMemo memo;
038
039    ArrayList<String[]> locolist = new ArrayList<String[]>();
040
041    LocoDataModel(int row, int column, jmri.jmrix.tams.TamsSystemConnectionMemo memo) {
042        this.memo = memo;
043        TamsMessage m = new TamsMessage("xLOCDUMP");
044        memo.getTrafficController().sendTamsMessage(m, this);
045    }
046
047    /**
048     * Returns the number of rows to be displayed. This can vary depending on
049     * whether only active rows are displayed, and whether the system slots
050     * should be displayed.
051     * <p>
052     * This should probably use a local cache instead of counting/searching each
053     * time.
054     */
055    @Override
056    public int getRowCount() {
057        return locolist.size();
058    }
059
060    @Override
061    public int getColumnCount() {
062        return NUMCOLUMN;
063    }
064
065    @Override
066    public String getColumnName(int col) {
067        switch (col) {
068            case ADDRCOLUMN:
069                return Bundle.getMessage("ColAddress");
070            case SPDCOLUMN:
071                return Bundle.getMessage("ColSteps");
072            case FMTCOLUMN:
073                return Bundle.getMessage("ColFormat");
074            case NAMECOLUMN:
075                return Bundle.getMessage("ColName");
076            case DELCOLUMN:
077                return Bundle.getMessage("ColDelete"); // TODO reuse existing key in jmri.NBBundle
078            default:
079                return "unknown";
080        }
081    }
082
083    @Override
084    public Class<?> getColumnClass(int col) {
085
086        switch (col) {
087            case DELCOLUMN:
088                return JButton.class;
089            default:
090                return String.class;
091        }
092    }
093
094    @Override
095    public boolean isCellEditable(int row, int col) {
096        switch (col) {
097            case DELCOLUMN:
098                return true;
099            default:
100                return false;
101        }
102    }
103
104    @Override
105    public Object getValueAt(int row, int col) {
106        if (locolist.size() == 0) {
107            return null;
108        }
109        String[] loco = locolist.get(row);
110        try {
111            switch (col) {
112                case ADDRCOLUMN:  //
113                    return loco[0];//Integer.valueOf(s.locoAddr());
114                case SPDCOLUMN:  //
115                    return loco[1];
116                case FMTCOLUMN:  //
117                    return loco[2];
118                case NAMECOLUMN:  //
119                    return loco[3];
120                case DELCOLUMN:
121                    return "delete"; // NOI18N
122                default:
123                    log.error("internal state inconsistent with table request for {} {}", row, col);
124                    return null;
125            }
126        } catch (RuntimeException ex) {
127
128        }
129        return null;
130    }
131
132    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
133                    justification="better to keep cases in column order rather than to combine")
134    public int getPreferredWidth(int col) {
135        switch (col) {
136            case ADDRCOLUMN:
137                return new JTextField(5).getPreferredSize().width;
138            case SPDCOLUMN:
139                return new JTextField(6).getPreferredSize().width;
140            case FMTCOLUMN:
141                return new JTextField(6).getPreferredSize().width;
142            case NAMECOLUMN:
143                return new JTextField(12).getPreferredSize().width;
144            case DELCOLUMN:
145                return new JButton(Bundle.getMessage("DeleteLoco")).getPreferredSize().width;
146            default:
147                return new JLabel(" <unknown> ").getPreferredSize().width; // NOI18N
148        }
149    }
150
151    @Override
152    public void setValueAt(Object value, int row, int col) {
153        if (col == DELCOLUMN) {
154            deleteLoco(row);
155        }
156    }
157
158    //to delete a loco in the MC we have to clear all the locos in the list and add in the ones that we want to keep.
159    void deleteLoco(int row) {
160        locolist.remove(row);
161        TamsMessage m = new TamsMessage("xLOCCLEAR");
162        memo.getTrafficController().sendTamsMessage(m, this);
163        for (String[] loco : locolist) {
164            m = new TamsMessage("xLOCADD " + loco[0] + ", " + loco[1] + ", " + loco[2] + ", '" + loco[3] + "'");
165            memo.getTrafficController().sendTamsMessage(m, this);
166        }
167        m = new TamsMessage("xLOCDUMP");
168        memo.getTrafficController().sendTamsMessage(m, this);
169    }
170
171    /**
172     * Configure a table to have our standard rows and columns. This is
173     * optional, in that other table formats can use this table model. But we
174     * put it here to help keep it consistent.
175     *
176     * @param slotTable the table to configure
177     */
178    public void configureTable(JTable slotTable) {
179        // allow reordering of the columns
180        slotTable.getTableHeader().setReorderingAllowed(true);
181
182        // shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541)
183        slotTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
184
185        // resize columns as requested
186        for (int i = 0; i < slotTable.getColumnCount(); i++) {
187            int width = getPreferredWidth(i);
188            slotTable.getColumnModel().getColumn(i).setPreferredWidth(width);
189        }
190        slotTable.sizeColumnsToFit(-1);
191
192        // install a button renderer & editor in the "DISP" column for freeing a slot
193        setColumnToHoldButton(slotTable, LocoDataModel.DELCOLUMN);
194
195        // install a button renderer & editor in the "ESTOP" column for stopping a loco
196        //setColumnToHoldEStopButton(slotTable, LocoDataModel.ESTOPCOLUMN);
197    }
198
199    void setColumnToHoldButton(JTable slotTable, int column) {
200        TableColumnModel tcm = slotTable.getColumnModel();
201        // install the button renderers & editors in this column
202        ButtonRenderer buttonRenderer = new ButtonRenderer();
203        tcm.getColumn(column).setCellRenderer(buttonRenderer);
204        TableCellEditor buttonEditor = new ButtonEditor(new JButton());
205        tcm.getColumn(column).setCellEditor(buttonEditor);
206        // ensure the table rows, columns have enough room for buttons
207        slotTable.setRowHeight(new JButton("  " + getValueAt(1, column)).getPreferredSize().height);
208        slotTable.getColumnModel().getColumn(column)
209                .setPreferredWidth(new JButton("  " + getValueAt(1, column)).getPreferredSize().width);
210    }
211
212    void setColumnToHoldEStopButton(JTable slotTable, int column) {
213        TableColumnModel tcm = slotTable.getColumnModel();
214        // install the button renderers & editors in this column
215        ButtonRenderer buttonRenderer = new ButtonRenderer();
216        tcm.getColumn(column).setCellRenderer(buttonRenderer);
217        TableCellEditor buttonEditor = new ButtonEditor(new JButton()) {
218            @Override
219            public void mousePressed(JmriMouseEvent e) {
220                stopCellEditing();
221            }
222        };
223        tcm.getColumn(column).setCellEditor(buttonEditor);
224        // ensure the table rows, columns have enough room for buttons
225        slotTable.setRowHeight(new JButton("  " + getValueAt(1, column)).getPreferredSize().height);
226        slotTable.getColumnModel().getColumn(column)
227                .setPreferredWidth(new JButton("  " + getValueAt(1, column)).getPreferredSize().width);
228    }
229
230    public void dispose() {
231
232    }
233
234    @Override
235    public void message(TamsMessage m) {
236
237    }
238
239    @Override
240    public void reply(TamsReply r) {
241        if (r != null) {
242            if (r.match("xLOCADD") >= 0) {
243                //loco added so will request a fresh update
244                TamsMessage m = new TamsMessage("xLOCDUMP");
245                memo.getTrafficController().sendTamsMessage(m, this);
246            } else {
247                locolist = new ArrayList<>();
248                String msg = r.toString();
249                String[] rawlocolist = msg.split("\\r");
250                log.info("Raw loco list length: {}", rawlocolist.length);
251                for (String loco : rawlocolist) {
252                    log.info("Loco: {}", loco );
253                    if (!loco.equals("*END*")) {
254                        String[] locodetails = loco.split(",");
255                        locolist.add(locodetails);
256                    } else {
257                        break;
258                    }
259                }
260                fireTableDataChanged();
261            }
262        }
263    }
264
265    protected void addLoco(TamsMessage m) {
266        memo.getTrafficController().sendTamsMessage(m, this);
267    }
268
269    private final static Logger log = LoggerFactory.getLogger(LocoDataModel.class);
270
271}