001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.tagging.presets.items;
003
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.Collection;
007import java.util.List;
008
009import javax.swing.JPanel;
010
011import org.openstreetmap.josm.data.osm.OsmPrimitive;
012import org.openstreetmap.josm.data.osm.OsmUtils;
013import org.openstreetmap.josm.data.osm.Tag;
014import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport;
015import org.openstreetmap.josm.gui.widgets.IconTextCheckBox;
016import org.openstreetmap.josm.gui.widgets.QuadStateCheckBox;
017import org.openstreetmap.josm.tools.GBC;
018
019/**
020 * Checkbox type.
021 */
022public class Check extends KeyedItem {
023
024    /** the value to set when checked (default is "yes") */
025    public String value_on = OsmUtils.TRUE_VALUE; // NOSONAR
026    /** the value to set when unchecked (default is "no") */
027    public String value_off = OsmUtils.FALSE_VALUE; // NOSONAR
028    /** whether the off value is disabled in the dialog, i.e., only unset or yes are provided */
029    public boolean disable_off; // NOSONAR
030    /** "on" or "off" or unset (default is unset) */
031    public String default_; // only used for tagless objects // NOSONAR
032
033    private QuadStateCheckBox check;
034    private QuadStateCheckBox.State initialState;
035    private Boolean def;
036
037    @Override
038    public boolean addToPanel(JPanel p, TaggingPresetItemGuiSupport support) {
039
040        // find out if our key is already used in the selection.
041        final Usage usage = determineBooleanUsage(support.getSelected(), key);
042        final String oneValue = usage.map.isEmpty() ? null : usage.map.lastKey();
043        def = "on".equals(default_) ? Boolean.TRUE : "off".equals(default_) ? Boolean.FALSE : null;
044
045        initializeLocaleText(null);
046
047        if (usage.map.size() < 2 && (oneValue == null || value_on.equals(oneValue) || value_off.equals(oneValue))) {
048            if (def != null && !PROP_FILL_DEFAULT.get()) {
049                // default is set and filling default values feature is disabled - check if all primitives are untagged
050                for (OsmPrimitive s : support.getSelected()) {
051                    if (s.hasKeys()) {
052                        def = null;
053                    }
054                }
055            }
056
057            // all selected objects share the same value which is either true or false or unset,
058            // we can display a standard check box.
059            initialState = value_on.equals(oneValue) || Boolean.TRUE.equals(def)
060                    ? QuadStateCheckBox.State.SELECTED
061                    : value_off.equals(oneValue) || Boolean.FALSE.equals(def)
062                    ? QuadStateCheckBox.State.NOT_SELECTED
063                    : QuadStateCheckBox.State.UNSET;
064
065        } else {
066            def = null;
067            // the objects have different values, or one or more objects have something
068            // else than true/false. we display a quad-state check box
069            // in "partial" state.
070            initialState = QuadStateCheckBox.State.PARTIAL;
071        }
072
073        final List<QuadStateCheckBox.State> allowedStates = new ArrayList<>(4);
074        if (QuadStateCheckBox.State.PARTIAL == initialState)
075            allowedStates.add(QuadStateCheckBox.State.PARTIAL);
076        allowedStates.add(QuadStateCheckBox.State.SELECTED);
077        if (!disable_off || value_off.equals(oneValue))
078            allowedStates.add(QuadStateCheckBox.State.NOT_SELECTED);
079        allowedStates.add(QuadStateCheckBox.State.UNSET);
080        check = new QuadStateCheckBox(icon == null ? locale_text : null, initialState,
081                allowedStates.toArray(new QuadStateCheckBox.State[0]));
082        check.setPropertyText(key);
083        check.setState(check.getState()); // to update the tooltip text
084        check.setComponentPopupMenu(getPopupMenu());
085
086        if (icon != null) {
087            JPanel checkPanel = IconTextCheckBox.wrap(check, locale_text, getIcon());
088            checkPanel.applyComponentOrientation(support.getDefaultComponentOrientation());
089            p.add(checkPanel, GBC.eol()); // Do not fill, see #15104
090        } else {
091            check.applyComponentOrientation(support.getDefaultComponentOrientation());
092            p.add(check, GBC.eol()); // Do not fill, see #15104
093        }
094        check.addChangeListener(l -> support.fireItemValueModified(this, key, getValue()));
095        return true;
096    }
097
098    @Override
099    public void addCommands(List<Tag> changedTags) {
100        // if the user hasn't changed anything, don't create a command.
101        if (def == null && check.getState() == initialState) return;
102
103        // otherwise change things according to the selected value.
104        changedTags.add(new Tag(key, getValue()));
105    }
106
107    protected String getValue() {
108        return check.getState() == QuadStateCheckBox.State.SELECTED ? value_on :
109            check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? value_off :
110                null;
111    }
112
113    @Override
114    public MatchType getDefaultMatch() {
115        return MatchType.NONE;
116    }
117
118    @Override
119    public Collection<String> getValues() {
120        return disable_off ? Arrays.asList(value_on) : Arrays.asList(value_on, value_off);
121    }
122
123    @Override
124    public String toString() {
125        return "Check [key=" + key + ", text=" + text + ", "
126                + (locale_text != null ? "locale_text=" + locale_text + ", " : "")
127                + (value_on != null ? "value_on=" + value_on + ", " : "")
128                + (value_off != null ? "value_off=" + value_off + ", " : "")
129                + "default_=" + default_ + ", "
130                + (check != null ? "check=" + check + ", " : "")
131                + (initialState != null ? "initialState=" + initialState
132                        + ", " : "") + "def=" + def + ']';
133    }
134}