001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.imagery; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.GridBagLayout; 007import java.awt.LayoutManager; 008import java.awt.event.ItemEvent; 009import java.util.ArrayList; 010import java.util.Arrays; 011import java.util.Collection; 012import java.util.List; 013import java.util.Map; 014import java.util.concurrent.TimeUnit; 015 016import javax.swing.AbstractButton; 017import javax.swing.JCheckBox; 018import javax.swing.JComboBox; 019import javax.swing.JLabel; 020import javax.swing.JPanel; 021import javax.swing.JSpinner; 022import javax.swing.SpinnerNumberModel; 023import javax.swing.event.DocumentEvent; 024import javax.swing.event.DocumentListener; 025import javax.swing.text.JTextComponent; 026 027import org.openstreetmap.josm.actions.ExpertToggleAction; 028import org.openstreetmap.josm.data.imagery.ImageryInfo; 029import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 030import org.openstreetmap.josm.data.imagery.TMSCachedTileLoaderJob; 031import org.openstreetmap.josm.gui.widgets.JosmTextArea; 032import org.openstreetmap.josm.gui.widgets.JosmTextField; 033import org.openstreetmap.josm.tools.GBC; 034import org.openstreetmap.josm.tools.Logging; 035 036/** 037 * An abstract imagery panel used to add WMS/TMS imagery sources. See implementations. 038 * @see AddTMSLayerPanel 039 * @see AddWMSLayerPanel 040 * @see AddWMTSLayerPanel 041 * @since 5617 042 */ 043public abstract class AddImageryPanel extends JPanel { 044 045 protected final JosmTextArea rawUrl = new JosmTextArea(3, 40).transferFocusOnTab(); 046 protected final JosmTextField name = new JosmTextField(); 047 048 protected final transient Collection<ContentValidationListener> listeners = new ArrayList<>(); 049 050 private final JCheckBox validGeoreference = new JCheckBox(tr("Is layer properly georeferenced?")); 051 private HeadersTable headersTable; 052 private JSpinner minimumCacheExpiry; 053 private JComboBox<String> minimumCacheExpiryUnit; 054 private TimeUnit currentUnit; 055 056 /** 057 * A listener notified when the validation status of this panel change. 058 * @since 10600 (functional interface) 059 */ 060 @FunctionalInterface 061 public interface ContentValidationListener { 062 /** 063 * Called when the validation status of this panel changed 064 * @param isValid true if the conditions required to close this panel are met 065 */ 066 void contentChanged(boolean isValid); 067 } 068 069 protected AddImageryPanel() { 070 this(new GridBagLayout()); 071 headersTable = new HeadersTable(); 072 minimumCacheExpiry = new JSpinner(new SpinnerNumberModel( 073 (Number) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get()), 074 0L, 075 Long.valueOf(Integer.MAX_VALUE), 076 1 077 )); 078 List<String> units = Arrays.asList(tr("seconds"), tr("minutes"), tr("hours"), tr("days")); 079 minimumCacheExpiryUnit = new JComboBox<>(units.toArray(new String[]{})); 080 currentUnit = TimeUnit.SECONDS; 081 minimumCacheExpiryUnit.addItemListener(e -> { 082 if (e.getStateChange() == ItemEvent.SELECTED) { 083 long newValue = 0; 084 switch (units.indexOf(e.getItem())) { 085 case 0: 086 newValue = currentUnit.toSeconds((long) minimumCacheExpiry.getValue()); 087 currentUnit = TimeUnit.SECONDS; 088 break; 089 case 1: 090 newValue = currentUnit.toMinutes((long) minimumCacheExpiry.getValue()); 091 currentUnit = TimeUnit.MINUTES; 092 break; 093 case 2: 094 newValue = currentUnit.toHours((long) minimumCacheExpiry.getValue()); 095 currentUnit = TimeUnit.HOURS; 096 break; 097 case 3: 098 newValue = currentUnit.toDays((long) minimumCacheExpiry.getValue()); 099 currentUnit = TimeUnit.DAYS; 100 break; 101 default: 102 Logging.warn("Unkown unit: " + units.indexOf(e.getItem())); 103 } 104 minimumCacheExpiry.setValue(newValue); 105 } 106 }); 107 108 109 } 110 111 protected void addCommonSettings() { 112 if (ExpertToggleAction.isExpert()) { 113 add(new JLabel(tr("Minimum cache expiry: "))); 114 add(minimumCacheExpiry); 115 add(minimumCacheExpiryUnit, GBC.eol()); 116 add(new JLabel(tr("Set custom HTTP headers (if needed):")), GBC.eop()); 117 add(headersTable, GBC.eol().fill()); 118 add(validGeoreference, GBC.eop().fill(GBC.HORIZONTAL)); 119 } 120 } 121 122 protected Map<String, String> getCommonHeaders() { 123 return headersTable.getHeaders(); 124 } 125 126 protected boolean getCommonIsValidGeoreference() { 127 return validGeoreference.isSelected(); 128 } 129 130 protected AddImageryPanel(LayoutManager layout) { 131 super(layout); 132 registerValidableComponent(name); 133 } 134 135 protected final void registerValidableComponent(AbstractButton component) { 136 component.addChangeListener(e -> notifyListeners()); 137 } 138 139 protected final void registerValidableComponent(JTextComponent component) { 140 component.getDocument().addDocumentListener(new DocumentListener() { 141 @Override 142 public void removeUpdate(DocumentEvent e) { 143 notifyListeners(); 144 } 145 146 @Override 147 public void insertUpdate(DocumentEvent e) { 148 notifyListeners(); 149 } 150 151 @Override 152 public void changedUpdate(DocumentEvent e) { 153 notifyListeners(); 154 } 155 }); 156 } 157 158 protected abstract ImageryInfo getImageryInfo(); 159 160 protected static String sanitize(String s) { 161 return s.replaceAll("[\r\n]+", "").trim(); 162 } 163 164 protected static String sanitize(String s, ImageryType type) { 165 String ret = s; 166 String imageryType = type.getTypeString() + ':'; 167 if (ret.startsWith(imageryType)) { 168 // remove ImageryType from URL 169 ret = ret.substring(imageryType.length()); 170 } 171 return sanitize(ret); 172 } 173 174 protected final String getImageryName() { 175 return sanitize(name.getText()); 176 } 177 178 protected final String getImageryRawUrl() { 179 return sanitize(rawUrl.getText()); 180 } 181 182 protected abstract boolean isImageryValid(); 183 184 /** 185 * Registers a new ContentValidationListener 186 * @param l The new ContentValidationListener that will be notified of validation status changes 187 */ 188 public final void addContentValidationListener(ContentValidationListener l) { 189 if (l != null) { 190 listeners.add(l); 191 } 192 } 193 194 private void notifyListeners() { 195 for (ContentValidationListener l : listeners) { 196 l.contentChanged(isImageryValid()); 197 } 198 } 199}