001package jmri.jmrit.beantable;
002
003import java.awt.Color;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import javax.annotation.Nonnull;
007import javax.swing.*;
008
009import jmri.InstanceManager;
010import jmri.Manager;
011import jmri.Reporter;
012import jmri.ReporterManager;
013import jmri.UserPreferencesManager;
014import jmri.swing.ManagerComboBox;
015import jmri.swing.SystemNameValidator;
016import jmri.util.JmriJFrame;
017import jmri.util.swing.JmriJOptionPane;
018
019/**
020 * Swing action to create and register a ReporterTable GUI.
021 *
022 * @author Bob Jacobsen Copyright (C) 2003
023 */
024public class ReporterTableAction extends AbstractTableAction<Reporter> {
025
026    /**
027     * Create an action with a specific title.
028     * <p>
029     * Note that the argument is the Action title, not the title of the
030     * resulting frame. Perhaps this should be changed?
031     *
032     * @param actionName title of the action
033     */
034    public ReporterTableAction(String actionName) {
035        super(actionName);
036
037        // disable ourself if there is no primary Reporter manager available
038        if (reporterManager == null) {
039            super.setEnabled(false);
040        }
041    }
042
043    protected ReporterManager reporterManager = InstanceManager.getDefault(ReporterManager.class);
044
045    /**
046     * {@inheritDoc}
047     */
048    @Override
049    public void setManager(@Nonnull Manager<Reporter> man) {
050        if (man instanceof ReporterManager) {
051            reporterManager = (ReporterManager) man;
052        }
053    }
054
055    public ReporterTableAction() {
056        this(Bundle.getMessage("TitleReporterTable"));
057    }
058
059    /**
060     * Create the JTable DataModel, along with the changes for the specific case
061     * of Reporters.
062     */
063    @Override
064    protected void createModel() {
065        m = new ReporterTableDataModel(reporterManager);
066    }
067
068    /**
069     * {@inheritDoc}
070     */
071    @Override
072    protected void setTitle() {
073        f.setTitle(Bundle.getMessage("TitleReporterTable"));
074    }
075
076    /**
077     * {@inheritDoc}
078     */
079    @Override
080    protected String helpTarget() {
081        return "package.jmri.jmrit.beantable.ReporterTable";
082    }
083
084    private JmriJFrame addFrame = null;
085    private final JTextField hardwareAddressTextField = new JTextField(20);
086    private final JTextField userNameTextField = new JTextField(20);
087    private final ManagerComboBox<Reporter> prefixBox = new ManagerComboBox<>();
088    private final SpinnerNumberModel rangeSpinner = new SpinnerNumberModel(1, 1, 100, 1); // maximum 100 items
089    private final JSpinner numberToAddSpinner = new JSpinner(rangeSpinner);
090    private final JCheckBox rangeCheckBox = new JCheckBox(Bundle.getMessage("AddRangeBox"));
091    private final String systemSelectionCombo = this.getClass().getName() + ".SystemSelected";
092    private JButton addButton;
093    private final JLabel statusBarLabel = new JLabel(Bundle.getMessage("HardwareAddStatusEnter"), JLabel.LEADING);
094    private final String userNameError = this.getClass().getName() + ".DuplicateUserName"; // only used in this package
095    private Manager<Reporter> connectionChoice = null;
096    private UserPreferencesManager pref;
097    private SystemNameValidator hardwareAddressValidator;
098
099    /**
100     * {@inheritDoc}
101     */
102    @Override
103    protected void addPressed(ActionEvent e) {
104        pref = InstanceManager.getDefault(UserPreferencesManager.class);
105        if (addFrame == null) {
106            addFrame = new JmriJFrame(Bundle.getMessage("TitleAddReporter"), false, true);
107            addFrame.addHelpMenu("package.jmri.jmrit.beantable.ReporterAddEdit", true);
108            ActionListener createListener = this::createPressed;
109            ActionListener cancelListener = this::cancelPressed;
110            ActionListener rangeListener = this::canAddRange;
111            configureManagerComboBox(prefixBox, reporterManager, ReporterManager.class);
112            userNameTextField.setName("userName"); // NOI18N
113            prefixBox.setName("prefixBox"); // NOI18N
114            addButton = new JButton(Bundle.getMessage("ButtonCreate"));
115            addButton.addActionListener(createListener);
116
117            if (hardwareAddressValidator==null){
118                hardwareAddressValidator = new SystemNameValidator(hardwareAddressTextField, java.util.Objects.requireNonNull(prefixBox.getSelectedItem()), true);
119            } else {
120                hardwareAddressValidator.setManager(prefixBox.getSelectedItem());
121            }
122
123            // create panel
124            addFrame.add(new AddNewHardwareDevicePanel(hardwareAddressTextField, hardwareAddressValidator, userNameTextField, prefixBox,
125                    numberToAddSpinner, rangeCheckBox, addButton, cancelListener, rangeListener, statusBarLabel));
126            // tooltip for hardwareAddressTextField will be assigned next by canAddRange()
127            canAddRange(null);
128        }
129        hardwareAddressTextField.setName("sysName"); // for GUI test NOI18N
130        hardwareAddressTextField.setName("hwAddressTextField"); // for GUI test NOI18N
131        addButton.setName("createButton"); // for GUI test NOI18N
132        // reset statusBarLabel text
133        statusBarLabel.setText(Bundle.getMessage("HardwareAddStatusEnter"));
134        statusBarLabel.setForeground(Color.gray);
135        addFrame.setEscapeKeyClosesWindow(true);
136        addFrame.getRootPane().setDefaultButton(addButton);
137        addFrame.pack();
138        addFrame.setVisible(true);
139    }
140
141    void cancelPressed(ActionEvent e) {
142        removePrefixBoxListener(prefixBox);
143        addFrame.setVisible(false);
144        addFrame.dispose();
145        addFrame = null;
146    }
147
148    /**
149     * Respond to Create new item button pressed on Add Reporter pane.
150     *
151     * @param e the click event
152     */
153    void createPressed(ActionEvent e) {
154
155        int numberOfReporters = 1;
156
157        if (rangeCheckBox.isSelected()) {
158            numberOfReporters = (Integer) numberToAddSpinner.getValue();
159        }
160        if (numberOfReporters >= 65 // limited by JSpinnerModel to 100
161            && JmriJOptionPane.showConfirmDialog(addFrame,
162                Bundle.getMessage("WarnExcessBeans", Bundle.getMessage("Reporters"), numberOfReporters),
163                Bundle.getMessage("WarningTitle"),
164                JmriJOptionPane.YES_NO_OPTION ) != JmriJOptionPane.YES_OPTION ) {
165            return;
166        }
167        String rName;
168        String reporterPrefix = prefixBox.getSelectedItem().getSystemPrefix(); // Add "R" later
169        String curAddress = hardwareAddressTextField.getText();
170        // initial check for empty entry
171        if (curAddress.isEmpty()) {
172            statusBarLabel.setText(Bundle.getMessage("WarningEmptyHardwareAddress"));
173            statusBarLabel.setForeground(Color.red);
174            hardwareAddressTextField.setBackground(Color.red);
175            return;
176        } else {
177            hardwareAddressTextField.setBackground(Color.white);
178        }
179
180        // Add some entry pattern checking, before assembling sName and handing it to the ReporterManager
181        StringBuilder statusMessage = new StringBuilder(
182            Bundle.getMessage("ItemCreateFeedback", Bundle.getMessage("BeanNameReporter")));
183        String uName = userNameTextField.getText();
184
185
186        // Compose the proposed system name from parts:
187        rName = reporterPrefix + reporterManager.typeLetter() + curAddress;
188
189       for (int x = 0; x < numberOfReporters; x++) {
190
191            // create the next reporter
192            Reporter r;
193            try {
194                r = reporterManager.provideReporter(rName);
195            } catch (IllegalArgumentException ex) {
196                // user input no good
197                handleCreateException(ex, rName); // displays message dialog to the user
198                return; // without creating
199            }
200
201            // handle setting user name
202            if (!uName.isEmpty()) {
203                if ((reporterManager.getByUserName(uName) == null)) {
204                    r.setUserName(uName);
205                } else {
206                    pref.showErrorMessage(Bundle.getMessage("ErrorTitle"),
207                            Bundle.getMessage("ErrorDuplicateUserName", uName), userNameError, "", false, true);
208                }
209            }
210
211            // add first and last names to statusMessage user feedback string
212            // only mention first and last of rangeCheckBox added
213            if (x == 0 || x == numberOfReporters - 1) {
214                statusMessage.append(" ").append(rName).append(" (").append(uName).append(")");
215            }
216            if (x == numberOfReporters - 2) {
217                statusMessage.append(" ").append(Bundle.getMessage("ItemCreateUpTo")).append(" ");
218            }
219
220            // except on last pass
221            if (x < numberOfReporters-1) {
222                // bump system name
223                try {
224                    rName = InstanceManager.getDefault(ReporterManager.class).getNextValidSystemName(r);
225                } catch (jmri.JmriException ex) {
226                    displayHwError(r.getSystemName(), ex);
227                    // directly add to statusBarLabel (but never called?)
228                    statusBarLabel.setText(Bundle.getMessage("ErrorConvertHW", rName));
229                    statusBarLabel.setForeground(Color.red);
230                    return;
231                }
232
233                // bump user name
234                if (!uName.isEmpty()) {
235                    uName = nextName(uName);
236                }
237            }
238            // end of for loop creating rangeCheckBox of Reporters
239        }
240        // provide success feedback to uName
241        statusBarLabel.setText(statusMessage.toString());
242        statusBarLabel.setForeground(Color.gray);
243
244        pref.setComboBoxLastSelection(systemSelectionCombo, prefixBox.getSelectedItem().getMemo().getUserName());
245        removePrefixBoxListener(prefixBox);
246        addFrame.setVisible(false);
247        addFrame.dispose();
248        addFrame = null;
249    }
250
251    private String addEntryToolTip;
252
253    /**
254     * Activate Add a rangeCheckBox option if manager accepts adding more than 1
255     * Reporter and set a manager specific tooltip on the AddNewHardwareDevice
256     * pane.
257     */
258    private void canAddRange(ActionEvent e) {
259        rangeCheckBox.setEnabled(false);
260        rangeCheckBox.setSelected(false);
261        if (prefixBox.getSelectedIndex() == -1) {
262            prefixBox.setSelectedIndex(0);
263        }
264        connectionChoice = prefixBox.getSelectedItem(); // store in Field for CheckedTextField
265        String systemPrefix = connectionChoice.getSystemPrefix();
266        rangeCheckBox.setEnabled(((ReporterManager) connectionChoice).allowMultipleAdditions(systemPrefix));
267        addEntryToolTip = connectionChoice.getEntryToolTip();
268        // show hwAddressTextField field tooltip in the Add Reporter pane that matches system connection selected from combobox
269        hardwareAddressTextField.setToolTipText(
270                Bundle.getMessage("AddEntryToolTipLine1",
271                        connectionChoice.getMemo().getUserName(),
272                        Bundle.getMessage("Reporters"),
273                        addEntryToolTip));
274        hardwareAddressValidator.setToolTipText(hardwareAddressTextField.getToolTipText());
275        hardwareAddressValidator.verify(hardwareAddressTextField);
276    }
277
278    void handleCreateException(Exception ex, String sysName) {
279        statusBarLabel.setText(ex.getLocalizedMessage());
280        statusBarLabel.setForeground(Color.red);
281        String err = Bundle.getMessage("ErrorBeanCreateFailed",
282            InstanceManager.getDefault(ReporterManager.class).getBeanTypeHandled(),sysName);
283        JmriJOptionPane.showMessageDialog(addFrame, err + "\n" + ex.getLocalizedMessage(),
284                err, JmriJOptionPane.ERROR_MESSAGE);
285    }
286
287    /**
288     * {@inheritDoc}
289     */
290    @Override
291    protected String getClassName() {
292        return ReporterTableAction.class.getName();
293    }
294
295    /**
296     * {@inheritDoc}
297     */
298    @Override
299    public String getClassDescription() {
300        return Bundle.getMessage("TitleReporterTable");
301    }
302
303    // private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ReporterTableAction.class);
304
305}