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 * @since 5617 025 */ 026public abstract class AddImageryPanel extends JPanel { 027 028 protected final JosmTextArea rawUrl = new JosmTextArea(3, 40).transferFocusOnTab(); 029 protected final JosmTextField name = new JosmTextField(); 030 031 protected final transient Collection<ContentValidationListener> listeners = new ArrayList<>(); 032 033 /** 034 * A listener notified when the validation status of this panel change. 035 * @since 10600 (functional interface) 036 */ 037 @FunctionalInterface 038 public interface ContentValidationListener { 039 /** 040 * Called when the validation status of this panel changed 041 * @param isValid true if the conditions required to close this panel are met 042 */ 043 void contentChanged(boolean isValid); 044 } 045 046 protected AddImageryPanel() { 047 this(new GridBagLayout()); 048 } 049 050 protected AddImageryPanel(LayoutManager layout) { 051 super(layout); 052 registerValidableComponent(name); 053 } 054 055 protected final void registerValidableComponent(AbstractButton component) { 056 component.addChangeListener(e -> notifyListeners()); 057 } 058 059 protected final void registerValidableComponent(JTextComponent component) { 060 component.getDocument().addDocumentListener(new DocumentListener() { 061 @Override 062 public void removeUpdate(DocumentEvent e) { 063 notifyListeners(); 064 } 065 066 @Override 067 public void insertUpdate(DocumentEvent e) { 068 notifyListeners(); 069 } 070 071 @Override 072 public void changedUpdate(DocumentEvent e) { 073 notifyListeners(); 074 } 075 }); 076 } 077 078 protected abstract ImageryInfo getImageryInfo(); 079 080 protected static String sanitize(String s) { 081 return s.replaceAll("[\r\n]+", "").trim(); 082 } 083 084 protected static String sanitize(String s, ImageryType type) { 085 String ret = s; 086 String imageryType = type.getTypeString() + ':'; 087 if (ret.startsWith(imageryType)) { 088 // remove ImageryType from URL 089 ret = ret.substring(imageryType.length()); 090 } 091 return sanitize(ret); 092 } 093 094 protected final String getImageryName() { 095 return sanitize(name.getText()); 096 } 097 098 protected final String getImageryRawUrl() { 099 return sanitize(rawUrl.getText()); 100 } 101 102 protected abstract boolean isImageryValid(); 103 104 /** 105 * Registers a new ContentValidationListener 106 * @param l The new ContentValidationListener that will be notified of validation status changes 107 */ 108 public final void addContentValidationListener(ContentValidationListener l) { 109 if (l != null) { 110 listeners.add(l); 111 } 112 } 113 114 private void notifyListeners() { 115 for (ContentValidationListener l : listeners) { 116 l.contentChanged(isImageryValid()); 117 } 118 } 119}