001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.imagery;
003
004import java.awt.GridBagLayout;
005import java.awt.LayoutManager;
006import java.util.ArrayList;
007import java.util.Collection;
008
009import javax.swing.AbstractButton;
010import javax.swing.JPanel;
011import javax.swing.event.DocumentEvent;
012import javax.swing.event.DocumentListener;
013import javax.swing.text.JTextComponent;
014
015import org.openstreetmap.josm.data.imagery.ImageryInfo;
016import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
017import org.openstreetmap.josm.gui.widgets.JosmTextArea;
018import org.openstreetmap.josm.gui.widgets.JosmTextField;
019
020/**
021 * An abstract imagery panel used to add WMS/TMS imagery sources. See implementations.
022 * @see AddTMSLayerPanel
023 * @see AddWMSLayerPanel
024 * @see AddWMTSLayerPanel
025 * @since 5617
026 */
027public abstract class AddImageryPanel extends JPanel {
028
029    protected final JosmTextArea rawUrl = new JosmTextArea(3, 40).transferFocusOnTab();
030    protected final JosmTextField name = new JosmTextField();
031
032    protected final transient Collection<ContentValidationListener> listeners = new ArrayList<>();
033
034    /**
035     * A listener notified when the validation status of this panel change.
036     * @since 10600 (functional interface)
037     */
038    @FunctionalInterface
039    public interface ContentValidationListener {
040        /**
041         * Called when the validation status of this panel changed
042         * @param isValid true if the conditions required to close this panel are met
043         */
044        void contentChanged(boolean isValid);
045    }
046
047    protected AddImageryPanel() {
048        this(new GridBagLayout());
049    }
050
051    protected AddImageryPanel(LayoutManager layout) {
052        super(layout);
053        registerValidableComponent(name);
054    }
055
056    protected final void registerValidableComponent(AbstractButton component) {
057        component.addChangeListener(e -> notifyListeners());
058    }
059
060    protected final void registerValidableComponent(JTextComponent component) {
061        component.getDocument().addDocumentListener(new DocumentListener() {
062            @Override
063            public void removeUpdate(DocumentEvent e) {
064                notifyListeners();
065            }
066
067            @Override
068            public void insertUpdate(DocumentEvent e) {
069                notifyListeners();
070            }
071
072            @Override
073            public void changedUpdate(DocumentEvent e) {
074                notifyListeners();
075            }
076        });
077    }
078
079    protected abstract ImageryInfo getImageryInfo();
080
081    protected static String sanitize(String s) {
082        return s.replaceAll("[\r\n]+", "").trim();
083    }
084
085    protected static String sanitize(String s, ImageryType type) {
086        String ret = s;
087        String imageryType = type.getTypeString() + ':';
088        if (ret.startsWith(imageryType)) {
089            // remove ImageryType from URL
090            ret = ret.substring(imageryType.length());
091        }
092        return sanitize(ret);
093    }
094
095    protected final String getImageryName() {
096        return sanitize(name.getText());
097    }
098
099    protected final String getImageryRawUrl() {
100        return sanitize(rawUrl.getText());
101    }
102
103    protected abstract boolean isImageryValid();
104
105    /**
106     * Registers a new ContentValidationListener
107     * @param l The new ContentValidationListener that will be notified of validation status changes
108     */
109    public final void addContentValidationListener(ContentValidationListener l) {
110        if (l != null) {
111            listeners.add(l);
112        }
113    }
114
115    private void notifyListeners() {
116        for (ContentValidationListener l : listeners) {
117            l.contentChanged(isImageryValid());
118        }
119    }
120}