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}