001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.display;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.GridBagLayout;
008import java.util.ArrayList;
009import java.util.Comparator;
010import java.util.List;
011import java.util.Locale;
012
013import javax.swing.Box;
014import javax.swing.DefaultListCellRenderer;
015import javax.swing.JLabel;
016import javax.swing.JList;
017import javax.swing.JPanel;
018import javax.swing.ListCellRenderer;
019
020import org.openstreetmap.josm.gui.help.HelpUtil;
021import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
022import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
023import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
024import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
025import org.openstreetmap.josm.gui.widgets.JosmComboBox;
026import org.openstreetmap.josm.gui.widgets.JosmComboBoxModel;
027import org.openstreetmap.josm.spi.preferences.Config;
028import org.openstreetmap.josm.tools.GBC;
029import org.openstreetmap.josm.tools.I18n;
030import org.openstreetmap.josm.tools.LanguageInfo;
031
032/**
033 * Language preferences.
034 * @since 1065
035 */
036public class LanguagePreference extends DefaultTabPreferenceSetting {
037
038    private static final String LANGUAGE = "language";
039
040    /**
041     * Factory used to create a new {@code LanguagePreference}.
042     */
043    public static class Factory implements PreferenceSettingFactory {
044        @Override
045        public PreferenceSetting createPreferenceSetting() {
046            return new LanguagePreference();
047        }
048    }
049
050    LanguagePreference() {
051        super(/* ICON(preferences/) */ "language", tr("Language"), tr("Change the language of JOSM."));
052    }
053
054    /** the combo box with the available locales */
055    private JosmComboBox<Locale> langCombo;
056
057    @Override
058    public void addGui(final PreferenceTabbedPane gui) {
059        LanguageComboBoxModel model = new LanguageComboBoxModel();
060        // Selecting the language BEFORE the JComboBox listens to model changes speed up initialization by ~35ms (see #7386)
061        // See https://stackoverflow.com/questions/3194958/fast-replacement-for-jcombobox-basiccomboboxui
062        model.selectLanguage(Config.getPref().get(LANGUAGE));
063        langCombo = new JosmComboBox<>(model);
064        langCombo.setRenderer(new LanguageCellRenderer());
065
066        final JPanel panel = new JPanel(new GridBagLayout());
067        panel.add(new JLabel(tr("Language")), GBC.std().insets(20, 0, 0, 0));
068        panel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
069        panel.add(langCombo, GBC.eol().fill(GBC.HORIZONTAL));
070        panel.add(Box.createVerticalGlue(), GBC.eol().fill(GBC.BOTH));
071
072        createPreferenceTabWithScrollPane(gui, panel);
073    }
074
075    @Override
076    public boolean ok() {
077        if (langCombo.getSelectedItem() == null)
078            return Config.getPref().put(LANGUAGE, null);
079        else
080            return Config.getPref().put(LANGUAGE,
081                    LanguageInfo.getJOSMLocaleCode((Locale) langCombo.getSelectedItem()));
082    }
083
084    private static class LanguageComboBoxModel extends JosmComboBoxModel<Locale> {
085        private final List<Locale> data = new ArrayList<>();
086
087        LanguageComboBoxModel() {
088            data.add(0, null);
089            I18n.getAvailableTranslations()
090                    .sorted(Comparator.comparing(Locale::getDisplayLanguage))
091                    .forEachOrdered(data::add);
092        }
093
094        private void selectLanguage(String language) {
095            setSelectedItem(null);
096            if (language != null) {
097                String lang = LanguageInfo.getJavaLocaleCode(language);
098                data.stream()
099                        .filter(locale -> locale != null && locale.toString().equals(lang))
100                        .findFirst()
101                        .ifPresent(this::setSelectedItem);
102            }
103        }
104
105        @Override
106        public Locale getElementAt(int index) {
107            return data.get(index);
108        }
109
110        @Override
111        public int getSize() {
112            return data.size();
113        }
114    }
115
116    private static class LanguageCellRenderer implements ListCellRenderer<Locale> {
117        private final DefaultListCellRenderer dispatch;
118
119        /**
120         * Constructs a new {@code LanguageCellRenderer}.
121         */
122        LanguageCellRenderer() {
123            this.dispatch = new DefaultListCellRenderer();
124        }
125
126        @Override
127        public Component getListCellRendererComponent(JList<? extends Locale> list, Locale l,
128                int index, boolean isSelected, boolean cellHasFocus) {
129            return dispatch.getListCellRendererComponent(list,
130                    l == null
131                            ? tr("Default (Auto determined)")
132                            : LanguageInfo.getDisplayName(l),
133                    index, isSelected, cellHasFocus);
134        }
135    }
136
137    @Override
138    public boolean isExpert() {
139        return false;
140    }
141
142    @Override
143    public String getHelpContext() {
144        return HelpUtil.ht("/Preferences/LanguagePreference");
145    }
146}