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