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.widgets.QuadStateCheckBox;
015import org.openstreetmap.josm.tools.GBC;
016
017/**
018 * Checkbox type.
019 */
020public class Check extends KeyedItem {
021
022    /** The localized version of {@link #text}. */
023    public String locale_text; // NOSONAR
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, Collection<OsmPrimitive> sel, boolean presetInitiallyMatches) {
039
040        // find out if our key is already used in the selection.
041        final Usage usage = determineBooleanUsage(sel, key);
042        final String oneValue = usage.values.isEmpty() ? null : usage.values.last();
043        def = "on".equals(default_) ? Boolean.TRUE : "off".equals(default_) ? Boolean.FALSE : null;
044
045        if (locale_text == null) {
046            locale_text = getLocaleText(text, text_context, null);
047        }
048
049        if (usage.values.size() < 2 && (oneValue == null || value_on.equals(oneValue) || value_off.equals(oneValue))) {
050            if (def != null && !PROP_FILL_DEFAULT.get()) {
051                // default is set and filling default values feature is disabled - check if all primitives are untagged
052                for (OsmPrimitive s : sel) {
053                    if (s.hasKeys()) {
054                        def = null;
055                    }
056                }
057            }
058
059            // all selected objects share the same value which is either true or false or unset,
060            // we can display a standard check box.
061            initialState = value_on.equals(oneValue) || Boolean.TRUE.equals(def)
062                    ? QuadStateCheckBox.State.SELECTED
063                    : value_off.equals(oneValue) || Boolean.FALSE.equals(def)
064                    ? QuadStateCheckBox.State.NOT_SELECTED
065                    : QuadStateCheckBox.State.UNSET;
066
067        } else {
068            def = null;
069            // the objects have different values, or one or more objects have something
070            // else than true/false. we display a quad-state check box
071            // in "partial" state.
072            initialState = QuadStateCheckBox.State.PARTIAL;
073        }
074
075        final List<QuadStateCheckBox.State> allowedStates = new ArrayList<>(4);
076        if (QuadStateCheckBox.State.PARTIAL == initialState)
077            allowedStates.add(QuadStateCheckBox.State.PARTIAL);
078        allowedStates.add(QuadStateCheckBox.State.SELECTED);
079        if (!disable_off || value_off.equals(oneValue))
080            allowedStates.add(QuadStateCheckBox.State.NOT_SELECTED);
081        allowedStates.add(QuadStateCheckBox.State.UNSET);
082        check = new QuadStateCheckBox(locale_text, initialState,
083                allowedStates.toArray(new QuadStateCheckBox.State[0]));
084        check.setPropertyText(key);
085        check.setState(check.getState()); // to update the tooltip text
086
087        p.add(check, GBC.eol()); // Do not fill, see #15104
088        return true;
089    }
090
091    @Override
092    public void addCommands(List<Tag> changedTags) {
093        // if the user hasn't changed anything, don't create a command.
094        if (def == null && check.getState() == initialState) return;
095
096        // otherwise change things according to the selected value.
097        changedTags.add(new Tag(key,
098                check.getState() == QuadStateCheckBox.State.SELECTED ? value_on :
099                    check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? value_off :
100                        null));
101    }
102
103    @Override
104    public MatchType getDefaultMatch() {
105        return MatchType.NONE;
106    }
107
108    @Override
109    public Collection<String> getValues() {
110        return disable_off ? Arrays.asList(value_on) : Arrays.asList(value_on, value_off);
111    }
112
113    @Override
114    public String toString() {
115        return "Check ["
116                + (locale_text != null ? "locale_text=" + locale_text + ", " : "")
117                + (value_on != null ? "value_on=" + value_on + ", " : "")
118                + (value_off != null ? "value_off=" + value_off + ", " : "")
119                + "default_=" + default_ + ", "
120                + (check != null ? "check=" + check + ", " : "")
121                + (initialState != null ? "initialState=" + initialState
122                        + ", " : "") + "def=" + def + ']';
123    }
124}