001package jmri.jmrit.operations.automation.gui;
002
003import java.awt.Dimension;
004import java.awt.GridBagLayout;
005
006import javax.swing.*;
007
008import jmri.InstanceManager;
009import jmri.jmrit.operations.OperationsFrame;
010import jmri.jmrit.operations.OperationsXml;
011import jmri.jmrit.operations.automation.*;
012import jmri.jmrit.operations.automation.actions.Action;
013import jmri.jmrit.operations.setup.Control;
014import jmri.jmrit.operations.setup.Setup;
015import jmri.swing.JTablePersistenceManager;
016import jmri.util.swing.JmriJOptionPane;
017
018/**
019 * Frame for user edit of a automation
020 *
021 * @author Dan Boudreau Copyright (C) 2016
022 */
023public class AutomationTableFrame extends OperationsFrame implements java.beans.PropertyChangeListener {
024
025    AutomationTableModel _automationTableModel = new AutomationTableModel();
026    JTable _automationTable = new JTable(_automationTableModel);
027    JScrollPane _automationPane;
028
029    AutomationManager automationManager;
030
031    Automation _automation = null;
032    AutomationItem _automationItem = null;
033
034    // labels
035    // major buttons
036    JButton stepActionButton = new JButton(Bundle.getMessage("StepAutomation"));
037    JButton runActionButton = new JButton(Bundle.getMessage("RunAutomation"));
038    JButton stopActionButton = new JButton(Bundle.getMessage("StopAutomation"));
039    JButton resumeActionButton = new JButton(Bundle.getMessage("ResumeAutomation"));
040
041    JButton addActionButton = new JButton(Bundle.getMessage("AddAction"));
042    JButton saveAutomationButton = new JButton(Bundle.getMessage("SaveAutomation"));
043    JButton deleteAutomationButton = new JButton(Bundle.getMessage("DeleteAutomation"));
044    JButton addAutomationButton = new JButton(Bundle.getMessage("AddAutomation"));
045
046    // radio buttons
047    JRadioButton addActionAtTopRadioButton = new JRadioButton(Bundle.getMessage("Top"));
048    JRadioButton addActionAtMiddleRadioButton = new JRadioButton(Bundle.getMessage("Middle"));
049    JRadioButton addActionAtBottomRadioButton = new JRadioButton(Bundle.getMessage("Bottom"));
050
051    // text field
052    JTextField automationNameTextField = new JTextField(20);
053    JTextField commentTextField = new JTextField(35);
054
055    // combo boxes
056
057    public static final int MAX_NAME_LENGTH = Control.max_len_string_automation_name;
058    public static final String NAME = Bundle.getMessage("Name");
059    public static final String DISPOSE = "dispose"; // NOI18N
060
061    public AutomationTableFrame(Automation automation) {
062        super();
063
064        _automation = automation;
065
066        // load managers
067        automationManager = InstanceManager.getDefault(AutomationManager.class);
068
069        // tool tips
070        stepActionButton.setToolTipText(Bundle.getMessage("TipStepAutomation"));
071        runActionButton.setToolTipText(Bundle.getMessage("TipRunAutomation"));
072        stopActionButton.setToolTipText(Bundle.getMessage("TipStopAutomation"));
073        resumeActionButton.setToolTipText(Bundle.getMessage("TipResumeAutomation"));
074
075        // Set up the jtable in a Scroll Pane..
076        _automationPane = new JScrollPane(_automationTable);
077        _automationPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
078
079        _automationTableModel.initTable(this, _automationTable, automation);
080        if (_automation != null) {
081            automationNameTextField.setText(_automation.getName());
082            commentTextField.setText(_automation.getComment());
083            setTitle(Bundle.getMessage("TitleAutomationEdit"));
084            enableButtons(true);
085        } else {
086            setTitle(Bundle.getMessage("TitleAutomationAdd"));
087            enableButtons(false);
088        }
089
090        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
091
092        // Layout the panel by rows
093        JPanel p1 = new JPanel();
094        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
095
096        JScrollPane p1Pane = new JScrollPane(p1);
097        p1Pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
098        p1Pane.setMinimumSize(new Dimension(300,
099                3 * automationNameTextField.getPreferredSize().height));
100        p1Pane.setMaximumSize(new Dimension(2000, 200));
101
102        // row 1a name
103        JPanel pName = new JPanel();
104        pName.setLayout(new GridBagLayout());
105        pName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Name")));
106        addItem(pName, automationNameTextField, 0, 0);
107
108        // row 1b comment
109        JPanel pComment = new JPanel();
110        pComment.setLayout(new GridBagLayout());
111        pComment.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment")));
112        addItem(pComment, commentTextField, 0, 0);
113
114        p1.add(pName);
115        p1.add(pComment);
116
117        // row 10
118        JPanel p3 = new JPanel();
119        p3.setLayout(new GridBagLayout());
120        p3.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("AddItem")));
121        p3.setMaximumSize(new Dimension(2000, 200));
122
123        addItem(p3, addActionButton, 1, 1);
124        addItem(p3, addActionAtTopRadioButton, 2, 1);
125        addItem(p3, addActionAtMiddleRadioButton, 3, 1);
126        addItem(p3, addActionAtBottomRadioButton, 4, 1);
127
128        ButtonGroup group = new ButtonGroup();
129        group.add(addActionAtTopRadioButton);
130        group.add(addActionAtMiddleRadioButton);
131        group.add(addActionAtBottomRadioButton);
132        addActionAtBottomRadioButton.setSelected(true);
133
134        // row 9 buttons
135        JPanel pControl = new JPanel();
136        pControl.setLayout(new GridBagLayout());
137        pControl.setBorder(BorderFactory.createTitledBorder(""));
138        pControl.setMaximumSize(new Dimension(2000, 200));
139
140        addItem(pControl, stepActionButton, 0, 0);
141        addItem(pControl, runActionButton, 1, 0);
142        addItem(pControl, resumeActionButton, 2, 0);
143        addItem(pControl, stopActionButton, 3, 0);
144
145        // row 11 buttons
146        JPanel pB = new JPanel();
147        pB.setLayout(new GridBagLayout());
148        pB.setBorder(BorderFactory.createTitledBorder(""));
149        pB.setMaximumSize(new Dimension(2000, 200));
150
151        // row 13
152        addItem(pB, deleteAutomationButton, 0, 0);
153        addItem(pB, addAutomationButton, 1, 0);
154        addItem(pB, saveAutomationButton, 3, 0);
155
156        getContentPane().add(p1Pane);
157        getContentPane().add(_automationPane);
158        getContentPane().add(p3);
159        getContentPane().add(pControl);
160        getContentPane().add(pB);
161
162        // setup buttons
163        addButtonAction(stepActionButton);
164        addButtonAction(runActionButton);
165        addButtonAction(stopActionButton);
166        addButtonAction(resumeActionButton);
167
168        addButtonAction(addActionButton);
169        addButtonAction(deleteAutomationButton);
170        addButtonAction(addAutomationButton);
171        addButtonAction(saveAutomationButton);
172
173        if (_automation != null) {
174            _automation.addPropertyChangeListener(this);
175        }
176
177        // build menu
178        JMenuBar menuBar = new JMenuBar();
179        JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools"));
180        menuBar.add(toolMenu);
181        toolMenu.add(new AutomationResetAction(this));
182        toolMenu.add(new AutomationCopyAction(automation));
183        setJMenuBar(menuBar);
184        addHelpMenu("package.jmri.jmrit.operations.Operations_Automation", true); // NOI18N
185
186        // set frame size and automation for display
187        initMinimumSize(new Dimension(Control.panelWidth700, Control.panelHeight400));
188    }
189
190    // Save, Delete, Add
191    @Override
192    public void buttonActionPerformed(java.awt.event.ActionEvent ae) {
193        stopCellEditing();
194        if (_automation != null) {
195            if (ae.getSource() == stepActionButton) {
196                _automation.step();
197            }
198            if (ae.getSource() == runActionButton) {
199                _automation.run();
200            }
201            if (ae.getSource() == stopActionButton) {
202                _automation.stop();
203            }
204            if (ae.getSource() == resumeActionButton) {
205                _automation.resume();
206            }
207        }
208        if (ae.getSource() == addActionButton) {
209            addNewAutomationItem();
210        }
211        if (ae.getSource() == saveAutomationButton) {
212            log.debug("automation save button activated");
213            Automation automation = automationManager.getAutomationByName(automationNameTextField.getText());
214            if (_automation == null && automation == null) {
215                saveNewAutomation();
216            } else {
217                if (automation != null && automation != _automation) {
218                    reportAutomationExists(Bundle.getMessage("save"));
219                    return;
220                }
221                saveAutomation();
222            }
223            if (Setup.isCloseWindowOnSaveEnabled()) {
224                dispose();
225            }
226        }
227        if (ae.getSource() == deleteAutomationButton) {
228            log.debug("automation delete button activated");
229            if (JmriJOptionPane.showConfirmDialog(this,
230                    Bundle.getMessage("DoYouWantToDeleteAutomation", automationNameTextField.getText()),
231                    Bundle.getMessage("DeleteAutomation?"),
232                    JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) {
233                return;
234            }
235            Automation automation = automationManager.getAutomationByName(automationNameTextField.getText());
236            if (automation == null) {
237                return;
238            }
239
240            automationManager.deregister(automation);
241            _automation = null;
242
243            enableButtons(false);
244            // save automation file
245            OperationsXml.save();
246        }
247        if (ae.getSource() == addAutomationButton) {
248            Automation automation = automationManager.getAutomationByName(automationNameTextField.getText());
249            if (automation != null) {
250                reportAutomationExists(Bundle.getMessage("add"));
251                return;
252            }
253            saveNewAutomation();
254        }
255    }
256
257    private void addNewAutomationItem() {
258        // add item to this automation
259        if (addActionAtTopRadioButton.isSelected()) {
260            _automation.addNewItem(0);
261        } else if (addActionAtBottomRadioButton.isSelected()) {
262            _automation.addItem();
263        } else {
264            // middle radio button selected
265            if (_automationTable.getSelectedRow() >= 0) {
266                int row = _automationTable.getSelectedRow();
267                _automation.addNewItem(row);
268                // we need to reselect the table since the content has changed
269                _automationTable.getSelectionModel().setSelectionInterval(row, row);
270            } else {
271                _automation.addNewItem(_automation.getSize() / 2);
272            }
273        }
274    }
275
276    private void saveNewAutomation() {
277        if (!checkName(Bundle.getMessage("add"))) {
278            return;
279        }
280        Automation automation = automationManager.newAutomation(automationNameTextField.getText());
281        _automationTableModel.initTable(this, _automationTable, automation);
282        _automation = automation;
283        _automation.addPropertyChangeListener(this);
284        // enable checkboxes
285        enableButtons(true);
286        saveAutomation();
287    }
288
289    private void saveAutomation() {
290        if (!checkName(Bundle.getMessage("save"))) {
291            return;
292        }
293        _automation.setName(automationNameTextField.getText());
294        _automation.setComment(commentTextField.getText());
295
296        // save automation file
297        OperationsXml.save();
298    }
299
300    private void stopCellEditing() {
301        if (_automationTable.isEditing()) {
302            log.debug("automation table edit true");
303            _automationTable.getCellEditor().stopCellEditing();
304        }
305    }
306
307    /**
308     *
309     * @return true if name is less than 26 characters
310     */
311    private boolean checkName(String s) {
312        if (automationNameTextField.getText().trim().isEmpty()) {
313            return false;
314        }
315        if (automationNameTextField.getText().length() > MAX_NAME_LENGTH) {
316            JmriJOptionPane.showMessageDialog(this,
317                    Bundle.getMessage("AutomationNameLengthMax", Integer.toString(MAX_NAME_LENGTH)),
318                    Bundle.getMessage("CanNotAutomation", s), JmriJOptionPane.ERROR_MESSAGE);
319            return false;
320        }
321        return true;
322    }
323
324    private void reportAutomationExists(String s) {
325        JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ReportExists"),
326                Bundle.getMessage("CanNotAutomation", s), JmriJOptionPane.ERROR_MESSAGE);
327    }
328
329    private void enableButtons(boolean enabled) {
330        enableControlButtons(enabled);
331
332        addActionButton.setEnabled(enabled);
333        addActionAtTopRadioButton.setEnabled(enabled);
334        addActionAtMiddleRadioButton.setEnabled(enabled);
335        addActionAtBottomRadioButton.setEnabled(enabled);
336        saveAutomationButton.setEnabled(enabled);
337        deleteAutomationButton.setEnabled(enabled);
338        _automationTable.setEnabled(enabled);
339        // the inverse!
340        addAutomationButton.setEnabled(!enabled);
341    }
342
343    private void enableControlButtons(boolean enabled) {
344        boolean b = enabled && _automation != null && _automation.getSize() > 0;
345        stepActionButton.setEnabled(b && !_automation.isActionRunning());
346        runActionButton.setEnabled(b && !_automation.isRunning());
347        stopActionButton.setEnabled(b);
348        resumeActionButton.setEnabled(b && !_automation.isRunning());
349    }
350
351    @Override
352    public void dispose() {
353        if (_automation != null) {
354            _automation.removePropertyChangeListener(this);
355        }
356        InstanceManager.getOptionalDefault(JTablePersistenceManager.class).ifPresent(tpm -> {
357            tpm.stopPersisting(_automationTable);
358        });
359        _automationTableModel.dispose();
360        super.dispose();
361    }
362
363    @Override
364    public void propertyChange(java.beans.PropertyChangeEvent e) {
365        //        if (Control.showProperty)
366        log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
367                .getNewValue());
368        if (e.getPropertyName().equals(Automation.LISTCHANGE_CHANGED_PROPERTY) ||
369                e.getPropertyName().equals(Automation.RUNNING_CHANGED_PROPERTY) ||
370                e.getPropertyName().equals(Action.ACTION_RUNNING_CHANGED_PROPERTY)) {
371            enableControlButtons(true);
372        }
373    }
374
375    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AutomationTableFrame.class);
376}