001package apps;
002
003import java.text.MessageFormat;
004import java.util.ArrayList;
005import java.util.HashMap;
006import java.util.List;
007import java.util.Map;
008import java.util.ResourceBundle;
009import javax.swing.JLabel;
010import jmri.Application;
011import jmri.ConfigureManager;
012import jmri.InstanceManager;
013import jmri.ShutDownManager;
014import jmri.UserPreferencesManager;
015import jmri.jmrix.ConnectionConfig;
016import jmri.jmrix.ConnectionConfigManager;
017import jmri.jmrix.JmrixConfigPane;
018import jmri.swing.ManagingPreferencesPanel;
019import jmri.swing.PreferencesPanel;
020import jmri.util.swing.JmriPanel;
021import jmri.util.swing.JmriJOptionPane;
022
023/**
024 * Basic configuration infrastructure, to be used by specific GUI
025 * implementations
026 *
027 * @author Bob Jacobsen Copyright (C) 2003, 2008, 2010
028 * @author Matthew Harris copyright (c) 2009
029 * @author Ken Cameron Copyright (C) 2011
030 */
031public class AppConfigBase extends JmriPanel {
032
033    /**
034     * All preferences panels handled, whether persisted or not.
035     */
036    protected HashMap<String, PreferencesPanel> preferencesPanels = new HashMap<>();
037
038    protected static final ResourceBundle rb = ResourceBundle.getBundle("apps.AppsConfigBundle");
039
040    /**
041     * Construct a configuration panel for inclusion in a preferences or
042     * configuration dialog with default number of connections.
043     */
044    public AppConfigBase() {
045    }
046
047    /**
048     * Detect duplicate connection types It depends on all connections have the
049     * first word be the same if they share the same type. So LocoNet ... is a
050     * fine example.
051     * <p>
052     * This also was broken when the names for systems were updated before JMRI
053     * 2.9.4, so it should be revisited.
054     *
055     * @return true if OK, false if duplicates present.
056     */
057    private boolean checkDups() {
058        Map<String, List<ConnectionConfig>> ports = new HashMap<>();
059        ConnectionConfig[] connections = InstanceManager.getDefault(ConnectionConfigManager.class).getConnections();
060        for (ConnectionConfig connection : connections) {
061            if (!connection.getDisabled()) {
062                String port = connection.getInfo();
063                if (!port.equals(JmrixConfigPane.NONE)) {
064                    if (!ports.containsKey(port)) {
065                        List<ConnectionConfig> arg1 = new ArrayList<>();
066                        arg1.add(connection);
067                        ports.put(port, arg1);
068                    } else {
069                        ports.get(port).add(connection);
070                    }
071                }
072            }
073        }
074        boolean ret = true;
075        /* one or more dups or NONE, lets see if it is dups */
076        for (Map.Entry<String, List<ConnectionConfig>> e : ports.entrySet()) {
077            if (e.getValue().size() > 1) {
078                /* dup port found */
079                ret = false;
080                StringBuilder nameB = new StringBuilder();
081                for (int n = 0; n < e.getValue().size(); n++) {
082                    nameB.append(e.getValue().get(n).getManufacturer());
083                    nameB.append("|");
084                }
085                String instanceNames = new String(nameB);
086                instanceNames = instanceNames.substring(0, instanceNames.lastIndexOf("|"));
087                instanceNames = instanceNames.replaceAll("[|]", ", ");
088                log.error("Duplicate ports found on: {} for port: {}", instanceNames, e.getKey());
089            }
090        }
091        return ret;
092    }
093
094    /**
095     * Checks to see if user selected a valid serial port
096     *
097     * @return true if okay
098     */
099    private boolean checkPortNames() {
100        for (ConnectionConfig connection : InstanceManager.getDefault(ConnectionConfigManager.class).getConnections()) {
101            String port = connection.getInfo();
102            if (port.equals(JmrixConfigPane.NONE_SELECTED) || port.equals(JmrixConfigPane.NO_PORTS_FOUND)) {
103                if (JmriJOptionPane.YES_OPTION != JmriJOptionPane.showConfirmDialog(
104                        null,
105                        MessageFormat.format(rb.getString("MessageSerialPortWarning"), new Object[]{port, connection.getConnectionName()}),
106                        rb.getString("MessageSerialPortNotValid"),
107                        JmriJOptionPane.YES_NO_OPTION,
108                        JmriJOptionPane.ERROR_MESSAGE)) {
109                    return false;
110                }
111            }
112        }
113        return true;
114    }
115
116    @Override
117    public void dispose() {
118        this.preferencesPanels.clear();
119    }
120
121    public void saveContents() {
122        // remove old prefs that are registered in ConfigManager
123        ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
124        if (cm != null) {
125            cm.removePrefItems();
126        }
127        // put the new GUI managedPreferences on the persistance list
128        this.getPreferencesPanels().values().stream().forEach((panel) -> {
129            this.registerWithConfigureManager(panel);
130        });
131        if (cm != null) {
132            cm.storePrefs();
133        }
134    }
135
136    private void registerWithConfigureManager(PreferencesPanel panel) {
137        if (panel.isPersistant()) {
138            ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
139            if (cm != null) {
140                cm.registerPref(panel);
141            }
142        }
143        if (panel instanceof ManagingPreferencesPanel) {
144            log.debug("Iterating over managed panels within {}/{}", panel.getPreferencesItemText(), panel.getTabbedPreferencesTitle());
145            ((ManagingPreferencesPanel) panel).getPreferencesPanels().stream().forEach((managed) -> {
146                log.debug("Registering {} with the ConfigureManager", managed.getClass().getName());
147                this.registerWithConfigureManager(managed);
148            });
149        }
150    }
151
152    /**
153     * Handle the Save button: Backup the file, write a new one, prompt for what
154     * to do next. To do that, the last step is to present a dialog box
155     * prompting the user to end the program, if required.
156     *
157     * @param restartRequired true if JMRI should prompt user to restart
158     */
159    public void savePressed(boolean restartRequired) {
160        // true if port name OK
161        if (!checkPortNames()) {
162            return;
163        }
164        // true if there arn't any duplicates
165        if (!checkDups()) {
166            if (!(JmriJOptionPane.showConfirmDialog(null, rb.getString("MessageLongDupsWarning"),
167                rb.getString("MessageShortDupsWarning"), JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION)) {
168                return;
169            }
170        }
171        saveContents();
172        final UserPreferencesManager p;
173        p = InstanceManager.getDefault(UserPreferencesManager.class);
174        p.resetChangeMade();
175        if (restartRequired && !InstanceManager.getDefault(ShutDownManager.class).isShuttingDown()) {
176            JLabel question = new JLabel(MessageFormat.format(rb.getString("MessageLongQuitWarning"), Application.getApplicationName()));
177            Object[] options = {rb.getString("RestartNow"), rb.getString("RestartLater")};
178            int retVal = JmriJOptionPane.showOptionDialog(this,
179                    question,
180                    MessageFormat.format(rb.getString("MessageShortQuitWarning"), Application.getApplicationName()),
181                    JmriJOptionPane.DEFAULT_OPTION,
182                    JmriJOptionPane.QUESTION_MESSAGE,
183                    null,
184                    options,
185                    null);
186            switch (retVal) {
187                case 0: // array position 0, restart Now
188                    dispose();
189                    Apps.handleRestart();
190                    break;
191                default:
192                    break;
193            }
194        }
195        // don't restart the program, just close the window
196        if (getTopLevelAncestor() != null) {
197            getTopLevelAncestor().setVisible(false);
198        }
199    }
200
201    public String getClassDescription() {
202        return rb.getString("Application");
203    }
204
205    public String getClassName() {
206        return AppConfigBase.class.getName();
207    }
208
209    /**
210     * @return the preferencesPanels
211     */
212    public HashMap<String, PreferencesPanel> getPreferencesPanels() {
213        return preferencesPanels;
214    }
215
216    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AppConfigBase.class);
217
218}