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