001package jmri.jmrit.operations.setup.gui;
002
003import java.awt.GridBagLayout;
004import java.awt.event.ActionEvent;
005
006import javax.swing.*;
007
008import jmri.InstanceManager;
009import jmri.jmrit.operations.OperationsXml;
010import jmri.jmrit.operations.locations.LocationManager;
011import jmri.jmrit.operations.locations.tools.LocationsByQuickServiceFrame;
012import jmri.jmrit.operations.setup.Setup;
013import jmri.jmrit.operations.trains.TrainManager;
014import jmri.util.swing.JmriJOptionPane;
015
016/**
017 * Frame for user edit of setup options
018 *
019 * @author Dan Boudreau Copyright (C) 2010, 2011, 2012, 2013, 2015, 2026
020 */
021public class OptionPanel extends OperationsPreferencesPanel {
022
023    // labels
024    // major buttons
025    JButton saveButton = new JButton(Bundle.getMessage("ButtonSave"));
026
027    // radio buttons
028    JRadioButton buildNormal = new JRadioButton(Bundle.getMessage("Normal"));
029    JRadioButton buildAggressive = new JRadioButton(Bundle.getMessage("Aggressive"));
030    JRadioButton buildOnTime = new JRadioButton(Bundle.getMessage("OnTime"));
031
032    // check boxes
033    JCheckBox routerCheckBox = new JCheckBox(Bundle.getMessage("EnableCarRouting"));
034    JCheckBox routerYardCheckBox = new JCheckBox(Bundle.getMessage("EnableCarRoutingYard"));
035    JCheckBox routerStagingCheckBox = new JCheckBox(Bundle.getMessage("EnableCarRoutingStaging"));
036    JCheckBox routerAllTrainsBox = new JCheckBox(Bundle.getMessage("AllTrains"));
037    JCheckBox routerRestrictBox = new JCheckBox(Bundle.getMessage("EnableTrackDestinationRestrictions"));
038
039    JCheckBox valueCheckBox = new JCheckBox(Bundle.getMessage("EnableValue"));
040    JCheckBox rfidCheckBox = new JCheckBox(Bundle.getMessage("EnableRfid"));
041    JCheckBox carLoggerCheckBox = new JCheckBox(Bundle.getMessage("EnableCarLogging"));
042    JCheckBox engineLoggerCheckBox = new JCheckBox(Bundle.getMessage("EnableEngineLogging"));
043    JCheckBox trainLoggerCheckBox = new JCheckBox(Bundle.getMessage("EnableTrainLogging"));
044
045    JCheckBox localInterchangeCheckBox = new JCheckBox(Bundle.getMessage("AllowLocalInterchange"));
046    JCheckBox localSpurCheckBox = new JCheckBox(Bundle.getMessage("AllowLocalSpur"));
047    JCheckBox localYardCheckBox = new JCheckBox(Bundle.getMessage("AllowLocalYard"));
048
049    JCheckBox trainIntoStagingCheckBox = new JCheckBox(Bundle.getMessage("TrainIntoStaging"));
050    JCheckBox stagingAvailCheckBox = new JCheckBox(Bundle.getMessage("StagingAvailable"));
051    JCheckBox stagingTurnCheckBox = new JCheckBox(Bundle.getMessage("AllowCarsToReturn"));
052    JCheckBox promptFromTrackStagingCheckBox = new JCheckBox(Bundle.getMessage("PromptFromStaging"));
053    JCheckBox promptToTrackStagingCheckBox = new JCheckBox(Bundle.getMessage("PromptToStaging"));
054    JCheckBox tryNormalStagingCheckBox = new JCheckBox(Bundle.getMessage("TryNormalStaging"));
055
056    JCheckBox generateCvsManifestCheckBox = new JCheckBox(Bundle.getMessage("GenerateCsvManifest"));
057    JCheckBox generateCvsSwitchListCheckBox = new JCheckBox(Bundle.getMessage("GenerateCsvSwitchList"));
058
059    JCheckBox enableVsdCheckBox = new JCheckBox(Bundle.getMessage("EnableVSD"));
060    JCheckBox saveTrainManifestCheckBox = new JCheckBox(Bundle.getMessage("SaveManifests"));
061
062    // text field
063    JTextField dwellTimeTextField = new JTextField(10);
064    JTextField rfidTextField = new JTextField(10);
065    JTextField valueTextField = new JTextField(10);
066
067    // combo boxes
068    JComboBox<Integer> numberPassesComboBox = new JComboBox<>();
069
070    public OptionPanel() {
071
072        // load checkboxes
073        localInterchangeCheckBox.setSelected(Setup.isLocalInterchangeMovesEnabled());
074        localSpurCheckBox.setSelected(Setup.isLocalSpurMovesEnabled());
075        localYardCheckBox.setSelected(Setup.isLocalYardMovesEnabled());
076        // staging options
077        trainIntoStagingCheckBox.setSelected(Setup.isStagingTrainCheckEnabled());
078        stagingAvailCheckBox.setSelected(Setup.isStagingTrackImmediatelyAvail());
079        stagingTurnCheckBox.setSelected(Setup.isStagingAllowReturnEnabled());
080        promptToTrackStagingCheckBox.setSelected(Setup.isStagingPromptToEnabled());
081        promptFromTrackStagingCheckBox.setSelected(Setup.isStagingPromptFromEnabled());
082        tryNormalStagingCheckBox.setSelected(Setup.isStagingTryNormalBuildEnabled());
083        // router
084        routerCheckBox.setSelected(Setup.isCarRoutingEnabled());
085        routerYardCheckBox.setSelected(Setup.isCarRoutingViaYardsEnabled());
086        routerStagingCheckBox.setSelected(Setup.isCarRoutingViaStagingEnabled());
087        routerAllTrainsBox.setSelected(!Setup.isOnlyActiveTrainsEnabled());
088        routerRestrictBox.setSelected(Setup.isCheckCarDestinationEnabled());
089        // logging options
090        carLoggerCheckBox.setSelected(Setup.isCarLoggerEnabled());
091        engineLoggerCheckBox.setSelected(Setup.isEngineLoggerEnabled());
092        trainLoggerCheckBox.setSelected(Setup.isTrainLoggerEnabled());
093        // save manifests
094        saveTrainManifestCheckBox.setSelected(Setup.isSaveTrainManifestsEnabled());
095
096        generateCvsManifestCheckBox.setSelected(Setup.isGenerateCsvManifestEnabled());
097        generateCvsSwitchListCheckBox.setSelected(Setup.isGenerateCsvSwitchListEnabled());
098        valueCheckBox.setSelected(Setup.isValueEnabled());
099        rfidCheckBox.setSelected(Setup.isRfidEnabled());
100        enableVsdCheckBox.setSelected(Setup.isVsdPhysicalLocationEnabled());
101
102        // load text fields
103        dwellTimeTextField.setText(Integer.toString(Setup.getDwellTime()));
104        rfidTextField.setText(Setup.getRfidLabel());
105        valueTextField.setText(Setup.getValueLabel());
106
107        // add tool tips
108        saveButton.setToolTipText(Bundle.getMessage("SaveToolTip"));
109        rfidTextField.setToolTipText(Bundle.getMessage("EnterNameRfidTip"));
110        valueTextField.setToolTipText(Bundle.getMessage("EnterNameValueTip"));
111        stagingTurnCheckBox.setToolTipText(Bundle.getMessage("AlsoAvailablePerTrain"));
112
113        // load combobox, allow 2 to 4 passes
114        for (int x = 2; x < 5; x++) {
115            numberPassesComboBox.addItem(x);
116        }
117
118        numberPassesComboBox.setSelectedItem(Setup.getNumberPasses());
119        padComboBox(numberPassesComboBox, 2);
120
121        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
122
123        JPanel panel = new JPanel();
124        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
125        JScrollPane panelPane = new JScrollPane(panel);
126
127        // Build Options panel
128        JPanel pBuild = new JPanel();
129        pBuild.setLayout(new BoxLayout(pBuild, BoxLayout.Y_AXIS));
130        pBuild.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutBuildOptions")));
131
132        JPanel pOpt = new JPanel();
133        pOpt.setLayout(new GridBagLayout());
134
135        addItem(pOpt, buildNormal, 1, 0);
136        addItem(pOpt, buildAggressive, 2, 0);
137        addItem(pOpt, buildOnTime, 3, 0);
138        pBuild.add(pOpt);
139
140        JPanel pPasses = new JPanel();
141        pPasses.setLayout(new GridBagLayout());
142        pPasses.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutNumberPasses")));
143        addItem(pPasses, numberPassesComboBox, 0, 0);
144        pBuild.add(pPasses);
145
146        JPanel pDwellTime = new JPanel();
147        pDwellTime.setLayout(new GridBagLayout());
148        pDwellTime.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutDwellTime")));
149        addItem(pDwellTime, dwellTimeTextField, 0, 0);
150        pBuild.add(pDwellTime);
151        
152        // Switcher Service
153        JPanel pSwitcher = new JPanel();
154        pSwitcher.setLayout(new GridBagLayout());
155        pSwitcher.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutSwitcherService")));
156
157        addItemLeft(pSwitcher, localInterchangeCheckBox, 1, 1);
158        addItemLeft(pSwitcher, localSpurCheckBox, 1, 2);
159        addItemLeft(pSwitcher, localYardCheckBox, 1, 3);
160
161        // Staging
162        JPanel pStaging = new JPanel();
163        pStaging.setLayout(new GridBagLayout());
164        pStaging.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutStaging")));
165
166        addItemLeft(pStaging, trainIntoStagingCheckBox, 1, 4);
167        addItemLeft(pStaging, stagingAvailCheckBox, 1, 5);
168        addItemLeft(pStaging, stagingTurnCheckBox, 1, 6);
169        addItemLeft(pStaging, promptFromTrackStagingCheckBox, 1, 7);
170        addItemLeft(pStaging, promptToTrackStagingCheckBox, 1, 8);
171        addItemLeft(pStaging, tryNormalStagingCheckBox, 1, 9);
172
173        // Router panel
174        JPanel pRouter = new JPanel();
175        pRouter.setLayout(new GridBagLayout());
176        pRouter.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutRouterOptions")));
177        addItemLeft(pRouter, routerCheckBox, 1, 0);
178        addItemLeft(pRouter, routerYardCheckBox, 1, 1);
179        addItemLeft(pRouter, routerStagingCheckBox, 1, 2);
180        addItemLeft(pRouter, routerAllTrainsBox, 1, 3);
181        addItemLeft(pRouter, routerRestrictBox, 1, 4);
182
183        // Logger panel
184        JPanel pLogger = new JPanel();
185        pLogger.setLayout(new GridBagLayout());
186        pLogger.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutLoggerOptions")));
187        addItemLeft(pLogger, engineLoggerCheckBox, 1, 0);
188        addItemLeft(pLogger, carLoggerCheckBox, 1, 1);
189        addItemLeft(pLogger, trainLoggerCheckBox, 1, 2);
190
191        // Custom Manifests and Switch Lists
192        JPanel pCustom = new JPanel();
193        pCustom.setLayout(new GridBagLayout());
194        pCustom.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutCustomManifests")));
195        addItemLeft(pCustom, generateCvsManifestCheckBox, 1, 0);
196        addItemLeft(pCustom, generateCvsSwitchListCheckBox, 1, 1);
197
198        // Options
199        JPanel pOption = new JPanel();
200        pOption.setLayout(new GridBagLayout());
201        pOption.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutAdditionalOptions")));
202        addItemLeft(pOption, saveTrainManifestCheckBox, 1, 1);
203        addItemLeft(pOption, valueCheckBox, 1, 2);
204        addItemLeft(pOption, valueTextField, 2, 2);
205        addItemLeft(pOption, rfidCheckBox, 1, 3);
206        addItemLeft(pOption, rfidTextField, 2, 3);
207        addItemLeft(pOption, enableVsdCheckBox, 1, 4);
208
209        // row 11
210        JPanel pControl = new JPanel();
211        pControl.setLayout(new GridBagLayout());
212        addItem(pControl, saveButton, 3, 9);
213
214        panel.add(pBuild);
215        panel.add(pSwitcher);
216        panel.add(pStaging);
217        panel.add(pRouter);
218        panel.add(pLogger);
219        panel.add(pCustom);
220        panel.add(pOption);
221
222        add(panelPane);
223        add(pControl);
224
225        // setup buttons
226        addButtonAction(saveButton);
227
228        // radio buttons
229        ButtonGroup buildGroup = new ButtonGroup();
230        buildGroup.add(buildNormal);
231        buildGroup.add(buildAggressive);
232        buildGroup.add(buildOnTime);
233
234        addRadioButtonAction(buildNormal);
235        addRadioButtonAction(buildAggressive);
236        addRadioButtonAction(buildOnTime);
237
238        // check boxes
239        addCheckBoxAction(routerCheckBox);
240        addCheckBoxAction(routerRestrictBox);
241        setRouterCheckBoxesEnabled();
242
243        setBuildOption();
244        enableComponents();
245    }
246
247    private void setBuildOption() {
248        buildNormal.setSelected(!Setup.isBuildAggressive());
249        buildAggressive.setSelected(Setup.isBuildAggressive());
250        buildOnTime.setSelected(Setup.isBuildOnTime());
251    }
252
253    private void enableComponents() {
254        // disable staging option if normal mode
255        stagingAvailCheckBox.setEnabled(!buildNormal.isSelected());
256        numberPassesComboBox.setEnabled(!buildNormal.isSelected());
257        dwellTimeTextField.setEnabled(Setup.isBuildOnTime() && buildOnTime.isSelected());
258        tryNormalStagingCheckBox.setEnabled(!buildNormal.isSelected());
259    }
260
261    @Override
262    public void radioButtonActionPerformed(ActionEvent ae) {
263        log.debug("radio button selected");
264        // can't change the build option if there are trains built
265        if (InstanceManager.getDefault(TrainManager.class).isAnyTrainBuilt()) {
266            setBuildOption(); // restore the correct setting
267            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("CanNotChangeBuild"),
268                    Bundle.getMessage("MustTerminateOrReset"), JmriJOptionPane.ERROR_MESSAGE);
269        }
270        enableComponents();
271        // Special case where there are train build failures that created work.
272        // Track reserve needs to cleaned up by reseting build failed trains
273        if (buildAggressive.isSelected() != Setup.isBuildAggressive() &&
274                InstanceManager.getDefault(LocationManager.class).hasWork()) {
275            InstanceManager.getDefault(TrainManager.class).resetBuildFailedTrains();
276        }
277    }
278
279    // Save button
280    @Override
281    public void buttonActionPerformed(ActionEvent ae) {
282        if (ae.getSource() == saveButton) {
283            this.savePreferences();
284            var topLevelAncestor = getTopLevelAncestor();
285            if (Setup.isCloseWindowOnSaveEnabled() && topLevelAncestor instanceof OptionFrame) {
286                ((OptionFrame) topLevelAncestor).dispose();
287            }
288        }
289    }
290
291    @Override
292    protected void checkBoxActionPerformed(ActionEvent ae) {
293        if (ae.getSource() == routerCheckBox) {
294            setRouterCheckBoxesEnabled();
295        }
296        if (ae.getSource() == routerRestrictBox && routerRestrictBox.isSelected()) {
297            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("WarnExtremeTrackDest"),
298                    Bundle.getMessage("WarnExtremeTitle"), JmriJOptionPane.WARNING_MESSAGE);
299        }
300    }
301
302    private void setRouterCheckBoxesEnabled() {
303        routerYardCheckBox.setEnabled(routerCheckBox.isSelected());
304        routerStagingCheckBox.setEnabled(routerCheckBox.isSelected());
305        routerAllTrainsBox.setEnabled(routerCheckBox.isSelected());
306        routerRestrictBox.setEnabled(routerCheckBox.isSelected());
307    }
308
309    @Override
310    public String getTabbedPreferencesTitle() {
311        return Bundle.getMessage("TitleOptions");
312    }
313
314    @Override
315    public String getPreferencesTooltip() {
316        return null;
317    }
318
319    @Override
320    public void savePreferences() {
321        // build option
322        boolean isBuildOnTime = Setup.isBuildOnTime();
323        Setup.setBuildAggressive(buildAggressive.isSelected() || buildOnTime.isSelected());
324        Setup.setNumberPasses((Integer) numberPassesComboBox.getSelectedItem());
325        Setup.setBuildOnTime(buildOnTime.isSelected());
326        if (isBuildOnTime) {
327            try {
328                Setup.setDwellTime(Integer.parseInt(dwellTimeTextField.getText()));
329            } catch (NumberFormatException e) {
330                log.error("Dwell Time {} must be a number", dwellTimeTextField.getText());
331            }
332        }
333        // local switcher options
334        Setup.setLocalInterchangeMovesEnabled(localInterchangeCheckBox.isSelected());
335        Setup.setLocalSpurMovesEnabled(localSpurCheckBox.isSelected());
336        Setup.setLocalYardMovesEnabled(localYardCheckBox.isSelected());
337        // Staging options
338        Setup.setStagingTrainCheckEnabled(trainIntoStagingCheckBox.isSelected());
339        Setup.setStagingTrackImmediatelyAvail(stagingAvailCheckBox.isSelected());
340        Setup.setStagingAllowReturnEnabled(stagingTurnCheckBox.isSelected());
341        Setup.setStagingPromptFromEnabled(promptFromTrackStagingCheckBox.isSelected());
342        Setup.setStagingPromptToEnabled(promptToTrackStagingCheckBox.isSelected());
343        Setup.setStagingTryNormalBuildEnabled(tryNormalStagingCheckBox.isSelected());
344        // Car routing enabled?
345        Setup.setCarRoutingEnabled(routerCheckBox.isSelected());
346        Setup.setCarRoutingViaYardsEnabled(routerYardCheckBox.isSelected());
347        Setup.setCarRoutingViaStagingEnabled(routerStagingCheckBox.isSelected());
348        Setup.setOnlyActiveTrainsEnabled(!routerAllTrainsBox.isSelected());
349        Setup.setCheckCarDestinationEnabled(routerRestrictBox.isSelected());
350        // Options
351        Setup.setGenerateCsvManifestEnabled(generateCvsManifestCheckBox.isSelected());
352        Setup.setGenerateCsvSwitchListEnabled(generateCvsSwitchListCheckBox.isSelected());
353        Setup.setSaveTrainManifestsEnabled(saveTrainManifestCheckBox.isSelected());
354        Setup.setValueEnabled(valueCheckBox.isSelected() && !valueTextField.getText().isBlank());
355        // value and rfid text fields can not be blank
356        Setup.setValueLabel(valueTextField.getText().isBlank() ? Bundle.getMessage("Value") : valueTextField.getText());
357        Setup.setRfidEnabled(rfidCheckBox.isSelected() && !rfidTextField.getText().isBlank());
358        Setup.setRfidLabel(rfidTextField.getText().isBlank() ? Bundle.getMessage("RFID") : rfidTextField.getText());
359        // Logging enabled?
360        Setup.setEngineLoggerEnabled(engineLoggerCheckBox.isSelected());
361        Setup.setCarLoggerEnabled(carLoggerCheckBox.isSelected());
362        Setup.setTrainLoggerEnabled(trainLoggerCheckBox.isSelected());
363        // VSD
364        Setup.setVsdPhysicalLocationEnabled(enableVsdCheckBox.isSelected());
365        // write the file
366        OperationsXml.save();
367        // bring up the quick service tool
368        if (!isBuildOnTime && buildOnTime.isSelected()) {
369            dwellTimeTextField.setText(Integer.toString(Setup.getDwellTime()));
370            dwellTimeTextField.setEnabled(buildOnTime.isSelected());
371            LocationsByQuickServiceFrame f = new LocationsByQuickServiceFrame();
372            f.initComponents();
373        }
374    }
375
376    @Override
377    public boolean isDirty() {
378        return !( // build option
379        Setup.isBuildAggressive() == buildAggressive.isSelected() &&
380                Setup.getNumberPasses() == (int) numberPassesComboBox.getSelectedItem()
381                // Local moves?
382                &&
383                Setup.isLocalInterchangeMovesEnabled() == localInterchangeCheckBox.isSelected() &&
384                Setup.isLocalSpurMovesEnabled() == localSpurCheckBox.isSelected() &&
385                Setup.isLocalYardMovesEnabled() == localYardCheckBox.isSelected()
386                // Staging options
387                &&
388                Setup.isStagingTrainCheckEnabled() == trainIntoStagingCheckBox.isSelected() &&
389                Setup.isStagingTrackImmediatelyAvail() == stagingAvailCheckBox.isSelected() &&
390                Setup.isStagingAllowReturnEnabled() == stagingTurnCheckBox.isSelected() &&
391                Setup.isStagingPromptFromEnabled() == promptFromTrackStagingCheckBox.isSelected() &&
392                Setup.isStagingPromptToEnabled() == promptToTrackStagingCheckBox.isSelected() &&
393                Setup.isStagingTryNormalBuildEnabled() == tryNormalStagingCheckBox.isSelected()
394                // Car routing enabled?
395                &&
396                Setup.isCarRoutingEnabled() == routerCheckBox.isSelected() &&
397                Setup.isCarRoutingViaYardsEnabled() == routerYardCheckBox.isSelected() &&
398                Setup.isCarRoutingViaStagingEnabled() == routerStagingCheckBox.isSelected() &&
399                Setup.isOnlyActiveTrainsEnabled() == !routerAllTrainsBox.isSelected() &&
400                Setup.isCheckCarDestinationEnabled() == routerRestrictBox.isSelected()
401                // Options
402                &&
403                Setup.isGenerateCsvManifestEnabled() == generateCvsManifestCheckBox.isSelected() &&
404                Setup.isGenerateCsvSwitchListEnabled() == generateCvsSwitchListCheckBox.isSelected() &&
405                Setup.isValueEnabled() == valueCheckBox.isSelected() &&
406                Setup.getValueLabel().equals(valueTextField.getText()) &&
407                Setup.isRfidEnabled() == rfidCheckBox.isSelected() &&
408                Setup.getRfidLabel().equals(rfidTextField.getText()) &&
409                Setup.isSaveTrainManifestsEnabled() == saveTrainManifestCheckBox.isSelected()
410                // Logging enabled?
411                &&
412                Setup.isEngineLoggerEnabled() == engineLoggerCheckBox.isSelected() &&
413                Setup.isCarLoggerEnabled() == carLoggerCheckBox.isSelected() &&
414                Setup.isTrainLoggerEnabled() == trainLoggerCheckBox.isSelected()
415                // VSD
416                &&
417                Setup.isVsdPhysicalLocationEnabled() == enableVsdCheckBox.isSelected());
418    }
419
420    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OptionPanel.class);
421
422}