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 Manager<Reporter> connectionChoice = null;
095    private UserPreferencesManager pref;
096    private SystemNameValidator hardwareAddressValidator;
097
098    /**
099     * {@inheritDoc}
100     */
101    @Override
102    protected void addPressed(ActionEvent e) {
103        pref = InstanceManager.getDefault(UserPreferencesManager.class);
104        if (addFrame == null) {
105            addFrame = new JmriJFrame(Bundle.getMessage("TitleAddReporter"), false, true);
106            addFrame.addHelpMenu("package.jmri.jmrit.beantable.ReporterAddEdit", true);
107            ActionListener createListener = this::createPressed;
108            ActionListener cancelListener = this::cancelPressed;
109            ActionListener rangeListener = this::canAddRange;
110            configureManagerComboBox(prefixBox, reporterManager, ReporterManager.class);
111            userNameTextField.setName("userName"); // NOI18N
112            prefixBox.setName("prefixBox"); // NOI18N
113            addButton = new JButton(Bundle.getMessage("ButtonCreate"));
114            addButton.addActionListener(createListener);
115
116            if (hardwareAddressValidator==null){
117                hardwareAddressValidator = new SystemNameValidator(hardwareAddressTextField, java.util.Objects.requireNonNull(prefixBox.getSelectedItem()), true);
118            } else {
119                hardwareAddressValidator.setManager(prefixBox.getSelectedItem());
120            }
121
122            // create panel
123            addFrame.add(new AddNewHardwareDevicePanel(hardwareAddressTextField, hardwareAddressValidator, userNameTextField, prefixBox,
124                    numberToAddSpinner, rangeCheckBox, addButton, cancelListener, rangeListener, statusBarLabel));
125            // tooltip for hardwareAddressTextField will be assigned next by canAddRange()
126            canAddRange(null);
127        }
128        hardwareAddressTextField.setName("sysName"); // for GUI test NOI18N
129        hardwareAddressTextField.setName("hwAddressTextField"); // for GUI test NOI18N
130        addButton.setName("createButton"); // for GUI test NOI18N
131        // reset statusBarLabel text
132        statusBarLabel.setText(Bundle.getMessage("HardwareAddStatusEnter"));
133        statusBarLabel.setForeground(Color.gray);
134        addFrame.setEscapeKeyClosesWindow(true);
135        addFrame.getRootPane().setDefaultButton(addButton);
136        addFrame.pack();
137        addFrame.setVisible(true);
138    }
139
140    void cancelPressed(ActionEvent e) {
141        removePrefixBoxListener(prefixBox);
142        addFrame.setVisible(false);
143        addFrame.dispose();
144        addFrame = null;
145    }
146
147    /**
148     * Respond to Create new item button pressed on Add Reporter pane.
149     *
150     * @param e the click event
151     */
152    void createPressed(ActionEvent e) {
153
154        int numberOfReporters = 1;
155
156        if (rangeCheckBox.isSelected()) {
157            numberOfReporters = (Integer) numberToAddSpinner.getValue();
158        }
159        if (numberOfReporters >= 65 // limited by JSpinnerModel to 100
160            && JmriJOptionPane.showConfirmDialog(addFrame,
161                Bundle.getMessage("WarnExcessBeans", Bundle.getMessage("Reporters"), numberOfReporters),
162                Bundle.getMessage("WarningTitle"),
163                JmriJOptionPane.YES_NO_OPTION ) != JmriJOptionPane.YES_OPTION ) {
164            return;
165        }
166        String rName;
167        String reporterPrefix = prefixBox.getSelectedItem().getSystemPrefix(); // Add "R" later
168        String curAddress = hardwareAddressTextField.getText();
169        // initial check for empty entry
170        if (curAddress.isEmpty()) {
171            statusBarLabel.setText(Bundle.getMessage("WarningEmptyHardwareAddress"));
172            statusBarLabel.setForeground(Color.red);
173            hardwareAddressTextField.setBackground(Color.red);
174            return;
175        } else {
176            hardwareAddressTextField.setBackground(Color.white);
177        }
178
179        // Add some entry pattern checking, before assembling sName and handing it to the ReporterManager
180        StringBuilder statusMessage = new StringBuilder(
181            Bundle.getMessage("ItemCreateFeedback", Bundle.getMessage("BeanNameReporter")));
182        String uName = userNameTextField.getText();
183
184
185        // Compose the proposed system name from parts:
186        rName = reporterPrefix + reporterManager.typeLetter() + curAddress;
187
188       for (int x = 0; x < numberOfReporters; x++) {
189
190            // create the next reporter
191            Reporter r;
192            try {
193                r = reporterManager.provideReporter(rName);
194            } catch (IllegalArgumentException ex) {
195                // user input no good
196                handleCreateException(ex, rName); // displays message dialog to the user
197                return; // without creating
198            }
199
200            // handle setting user name
201            if (!uName.isEmpty()) {
202                if ((reporterManager.getByUserName(uName) == null)) {
203                    r.setUserName(uName);
204                } else {
205                    pref.showErrorMessage(Bundle.getMessage("ErrorTitle"),
206                            Bundle.getMessage("ErrorDuplicateUserName", uName),
207                            getClassName(), "duplicateUserName", 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    @Override
304    public void setMessagePreferencesDetails() {
305        InstanceManager.getDefault(jmri.UserPreferencesManager.class).
306                setPreferenceItemDetails(getClassName(), "duplicateUserName", Bundle.getMessage("DuplicateUserNameWarn"));  // NOI18N
307        super.setMessagePreferencesDetails();
308    }
309
310    // private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ReporterTableAction.class);
311
312}