001package jmri.jmrit.operations.automation;
002
003import java.awt.Frame;
004import java.beans.PropertyChangeEvent;
005import java.beans.PropertyChangeListener;
006import java.util.*;
007
008import javax.swing.*;
009import javax.swing.table.TableCellEditor;
010import javax.swing.table.TableColumnModel;
011
012import jmri.InstanceManager;
013import jmri.jmrit.operations.OperationsXml;
014import jmri.jmrit.operations.setup.Control;
015import jmri.util.swing.JmriJOptionPane;
016import jmri.util.table.ButtonEditor;
017import jmri.util.table.ButtonRenderer;
018
019/**
020 * Table model allowing the edit and status of an automation used by operations.
021 *
022 * @author Daniel Boudreau Copyright (C) 2016
023 */
024public class AutomationsTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener {
025
026    AutomationManager automationManager; // There is only one manager
027
028    // Defines the columns
029    private static final int ID_COLUMN = 0;
030    private static final int NAME_COLUMN = ID_COLUMN + 1;
031    private static final int COMMENT_COLUMN = NAME_COLUMN + 1;
032    private static final int ACTION_COLUMN = COMMENT_COLUMN + 1;
033    private static final int STATUS_COLUMN = ACTION_COLUMN + 1;
034    private static final int RUN_COLUMN = STATUS_COLUMN + 1;
035    private static final int EDIT_COLUMN = RUN_COLUMN + 1;
036    private static final int DELETE_COLUMN = EDIT_COLUMN + 1;
037
038    private static final int HIGHEST_COLUMN = DELETE_COLUMN + 1;
039
040    public AutomationsTableModel() {
041        super();
042        automationManager = InstanceManager.getDefault(AutomationManager.class);
043        automationManager.addPropertyChangeListener(this);
044        updateList();
045    }
046
047    public final int SORTBYNAME = 1;
048    public final int SORTBYID = 2;
049
050    private int _sort = SORTBYNAME;
051
052    public void setSort(int sort) {
053        _sort = sort;
054        updateList();
055        fireTableDataChanged();
056    }
057
058    private void updateList() {
059        // first, remove listeners from the individual objects
060        removePropertyChangeAutomations();
061
062        if (_sort == SORTBYID) {
063            _sysList = automationManager.getAutomationsByIdList();
064        } else {
065            _sysList = automationManager.getAutomationsByNameList();
066        }
067        // and add them back in
068        for (Automation automation : _sysList) {
069            automation.addPropertyChangeListener(this);
070        }
071    }
072
073    List<Automation> _sysList = null;
074    JTable _table;
075
076    protected void initTable(AutomationsTableFrame frame, JTable table) {
077        _table = table;
078        // Install the button handlers
079        TableColumnModel tcm = table.getColumnModel();
080        ButtonRenderer buttonRenderer = new ButtonRenderer();
081        TableCellEditor buttonEditor = new ButtonEditor(new javax.swing.JButton());
082        tcm.getColumn(RUN_COLUMN).setCellRenderer(buttonRenderer);
083        tcm.getColumn(RUN_COLUMN).setCellEditor(buttonEditor);
084        tcm.getColumn(EDIT_COLUMN).setCellRenderer(buttonRenderer);
085        tcm.getColumn(EDIT_COLUMN).setCellEditor(buttonEditor);
086        tcm.getColumn(DELETE_COLUMN).setCellRenderer(buttonRenderer);
087        tcm.getColumn(DELETE_COLUMN).setCellEditor(buttonEditor);
088        table.setDefaultRenderer(JComboBox.class, new jmri.jmrit.symbolicprog.ValueRenderer());
089        table.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor());
090
091        // set column preferred widths
092        table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40);
093        table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(200);
094        table.getColumnModel().getColumn(COMMENT_COLUMN).setPreferredWidth(350);
095        table.getColumnModel().getColumn(ACTION_COLUMN).setPreferredWidth(250);
096        table.getColumnModel().getColumn(STATUS_COLUMN).setPreferredWidth(90);
097        table.getColumnModel().getColumn(RUN_COLUMN).setPreferredWidth(90);
098        table.getColumnModel().getColumn(EDIT_COLUMN).setPreferredWidth(70);
099        table.getColumnModel().getColumn(DELETE_COLUMN).setPreferredWidth(90);
100
101        frame.loadTableDetails(table);
102    }
103
104    @Override
105    public int getRowCount() {
106        return _sysList.size();
107    }
108
109    @Override
110    public int getColumnCount() {
111        return HIGHEST_COLUMN;
112    }
113
114    @Override
115    public String getColumnName(int col) {
116        switch (col) {
117            case ID_COLUMN:
118                return Bundle.getMessage("Id");
119            case NAME_COLUMN:
120                return Bundle.getMessage("Name");
121            case COMMENT_COLUMN:
122                return Bundle.getMessage("Comment");
123            case ACTION_COLUMN:
124                return Bundle.getMessage("Action");
125            case STATUS_COLUMN:
126                return Bundle.getMessage("Status");
127            case RUN_COLUMN:
128                return Bundle.getMessage("Run");
129            case EDIT_COLUMN:
130                return Bundle.getMessage("ButtonEdit");
131            case DELETE_COLUMN:
132                return Bundle.getMessage("ButtonDelete");
133            default:
134                return "unknown"; // NOI18N
135        }
136    }
137
138    @Override
139    public Class<?> getColumnClass(int col) {
140        switch (col) {
141            case ID_COLUMN:
142                return String.class;
143            case NAME_COLUMN:
144                return String.class;
145            case COMMENT_COLUMN:
146                return String.class;
147            case STATUS_COLUMN:
148                return String.class;
149            case ACTION_COLUMN:
150                return String.class;
151            case RUN_COLUMN:
152                return JButton.class;
153            case EDIT_COLUMN:
154                return JButton.class;
155            case DELETE_COLUMN:
156                return JButton.class;
157            default:
158                return null;
159        }
160    }
161
162    @Override
163    public boolean isCellEditable(int row, int col) {
164        switch (col) {
165            case RUN_COLUMN:
166            case EDIT_COLUMN:
167            case DELETE_COLUMN:
168                return true;
169            default:
170                return false;
171        }
172    }
173
174    @Override
175    public Object getValueAt(int row, int col) {
176        if (row >= getRowCount()) {
177            return "ERROR row " + row; // NOI18N
178        }
179        Automation automation = _sysList.get(row);
180        if (automation == null) {
181            return "ERROR automation unknown " + row; // NOI18N
182        }
183        switch (col) {
184            case ID_COLUMN:
185                return automation.getId();
186            case NAME_COLUMN:
187                return automation.getName();
188            case COMMENT_COLUMN:
189                return automation.getComment();
190            case ACTION_COLUMN:
191                return automation.getCurrentActionString();
192            case STATUS_COLUMN:
193                return automation.getActionStatus();
194            case RUN_COLUMN:
195                if (automation.isActionRunning())
196                    return Bundle.getMessage("Stop");
197                else if (automation.isReadyToRun())
198                    return Bundle.getMessage("Run");
199                else
200                    return Bundle.getMessage("Resume");
201            case EDIT_COLUMN:
202                return Bundle.getMessage("ButtonEdit");
203            case DELETE_COLUMN:
204                return Bundle.getMessage("ButtonDelete");
205            default:
206                return "unknown " + col; // NOI18N
207        }
208    }
209
210    @Override
211    public void setValueAt(Object value, int row, int col) {
212        switch (col) {
213            case RUN_COLUMN:
214                runAutomation(row);
215                break;
216            case EDIT_COLUMN:
217                editAutomation(row);
218                break;
219            case DELETE_COLUMN:
220                deleteAutomation(row);
221                break;
222            default:
223                break;
224        }
225    }
226
227    private void runAutomation(int row) {
228        Automation automation = _sysList.get(row);
229        SwingUtilities.invokeLater(() -> {
230            if (automation.isActionRunning())
231                automation.stop();
232            else
233                automation.resume();
234        });
235    }
236
237    Hashtable<String, AutomationTableFrame> automationEditFrames = new Hashtable<String, AutomationTableFrame>();
238
239    private void editAutomation(int row) {
240        log.debug("Edit automation");
241        Automation automation = _sysList.get(row);
242
243        // is the edit window already open?
244        if (automationEditFrames.containsKey(automation.getId())) {
245            AutomationTableFrame frame = automationEditFrames.get(automation.getId());
246            if (frame.isVisible()) {
247                frame.toFront();
248                frame.setExtendedState(Frame.NORMAL);
249                return; // done
250            }
251        }
252
253        // use invokeLater so new window appears on top
254        SwingUtilities.invokeLater(() -> {
255            AutomationTableFrame frame = new AutomationTableFrame(automation);
256            automationEditFrames.put(automation.getId(), frame);
257        });
258    }
259
260    private void deleteAutomation(int row) {
261        log.debug("Delete automation");
262        Automation automation = _sysList.get(row);
263        if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("DoYouWantToDeleteAutomation",
264                automation.getName()), Bundle.getMessage("DeleteAutomation?"),
265                JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) {
266            automationManager.deregister(automation);
267            OperationsXml.save();
268        }
269    }
270
271    private void removePropertyChangeAutomations() {
272        if (_sysList != null) {
273            for (Automation automation : _sysList) {
274                automation.removePropertyChangeListener(this);
275            }
276        }
277    }
278
279    public void dispose() {
280        Enumeration<String> en = automationEditFrames.keys();
281        while (en.hasMoreElements()) {
282            AutomationTableFrame frame = automationEditFrames.get(en.nextElement());
283            frame.dispose();
284        }
285
286        automationManager.removePropertyChangeListener(this);
287        removePropertyChangeAutomations();
288    }
289
290    // check for change in number of automations, or a change in a automation
291    @Override
292    public void propertyChange(PropertyChangeEvent e) {
293        if (Control.SHOW_PROPERTY) {
294            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
295                    .getNewValue());
296        }
297        if (e.getPropertyName().equals(AutomationManager.LISTLENGTH_CHANGED_PROPERTY)) {
298            updateList();
299            fireTableDataChanged();
300        } else if (e.getSource().getClass().equals(Automation.class)) {
301            Automation automation = (Automation) e.getSource();
302            int row = _sysList.indexOf(automation);
303            if (Control.SHOW_PROPERTY) {
304                log.debug("Update automation table row: {} name: {}", row, automation.getName());
305            }
306            if (row >= 0) {
307                fireTableRowsUpdated(row, row);
308            }
309        }
310    }
311
312    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AutomationsTableModel.class);
313
314}