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