// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.roadsigns;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.data.Preferences.pref;
import org.openstreetmap.josm.gui.ExtendedDialog;
import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog;
import org.openstreetmap.josm.io.CachedFile;
import org.openstreetmap.josm.plugins.Plugin;
import org.openstreetmap.josm.plugins.PluginInformation;
import org.openstreetmap.josm.plugins.roadsigns.RoadSignInputDialog.SettingsPanel;
import org.openstreetmap.josm.tools.Shortcut;
import org.openstreetmap.josm.tools.Utils;
import org.xml.sax.SAXException;
public class RoadSignsPlugin extends Plugin {
static PresetMetaData selectedPreset;
public static List<Sign> signs;
public static List<String> iconDirs;
public static RoadSignsPlugin plugin;
public static final PresetMetaData PRESET_BE = new PresetMetaData(
"BE", tr("Belgium"), "resource://data/roadsignpresetBE.xml", "resource://images/BE/");
public static final PresetMetaData PRESET_ES = new PresetMetaData(
"ES", tr("Spain"), "resource://data/roadsignpresetES.xml", "resource://images/ES/");
public static final PresetMetaData PRESET_DE = new PresetMetaData(
"DE", tr("Germany"), "resource://data/roadsignpresetDE.xml", "resource://images/DE/");
public static final PresetMetaData PRESET_PL = new PresetMetaData(
"PL", tr("Poland"), "resource://data/roadsignpresetPL.xml", "resource://images/PL/");
public static final PresetMetaData PRESET_SK = new PresetMetaData(
"SK", tr("Slovakia"), "resource://data/roadsignpresetSK.xml", "resource://images/SK/");
public static final Collection<PresetMetaData> DEFAULT_PRESETS = Arrays.asList(PRESET_BE, PRESET_ES, PRESET_DE, PRESET_PL, PRESET_SK);
public RoadSignsPlugin(PluginInformation info) {
super(info);
plugin = this;
registerAction();
}
public static File pluginDir() {
File dir = new File(plugin.getPluginDir());
if (!dir.exists()) {
dir.mkdirs();
}
return dir;
}
private void registerAction() {
JButton btn = new JButton(new RoadSignAction());
btn.setText(null);
btn.setBorder(BorderFactory.createEmptyBorder());
btn.setPreferredSize(new Dimension(18, 18));
PropertiesDialog.pluginHook.add(btn);
PropertiesDialog.pluginHook.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
PropertiesDialog.pluginHook.revalidate();
}
private static class RoadSignAction extends JosmAction {
RoadSignAction() {
super(tr("Roadsign tagging"), "pref/roadsigns-small", tr("Add tags by clicking on road signs"),
Shortcut.registerShortcut("plugin:roadsigns:dialog", tr("Roadsigns plugin: open dialog"),
KeyEvent.VK_Q, Shortcut.ALT_SHIFT), false);
}
@Override
public void actionPerformed(ActionEvent e) {
String code = Main.pref.get("plugin.roadsigns.preset.selection", null);
if (code == null) {
ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Settings"), new String[] {tr("Ok"), tr("Cancel")});
ed.setButtonIcons(new String[] {"ok", "cancel"});
SettingsPanel settings = new SettingsPanel(true, null);
ed.setContent(settings);
ed.showDialog();
if (ed.getValue() != 1) return;
try {
settings.apply();
} catch (IOException ex) {
return;
}
}
try {
loadSignPreset();
} catch (IOException ex) {
return;
}
RoadSignInputDialog input = new RoadSignInputDialog();
input.showDialog();
}
}
public static class PresetMetaData {
@pref public String code;
@pref public String display_name;
@pref public String preset_path;
@pref public String icon_path;
public PresetMetaData() {
}
public PresetMetaData(String country_code, String display_name, String preset_path, String icons_path) {
this.code = country_code;
this.display_name = display_name;
this.preset_path = preset_path;
this.icon_path = icons_path;
}
@Override
public String toString() {
return display_name;
}
}
public static void setSelectedPreset(PresetMetaData preset) throws IOException {
Main.pref.put("plugin.roadsigns.preset.selection", preset.code);
loadSignPreset();
}
public static List<PresetMetaData> getAvailablePresetsMetaData() {
List<PresetMetaData> presetsData = Main.pref.getListOfStructs("plugin.roadsigns.presets", DEFAULT_PRESETS, PresetMetaData.class);
String customFile = Main.pref.get("plugin.roadsigns.sources", null);
if (customFile == null) {
// for legacy reasons, try both string and collection preference type
Collection<String> customFiles = Main.pref.getCollection("plugin.roadsigns.sources", null);
if (customFiles != null && !customFiles.isEmpty()) {
customFile = customFiles.iterator().next();
}
}
if (customFile != null) {
// first check, if custom file preference has changed. If yes,
// change the current preset selection to custom directly
String lastCustomFile = Main.pref.get("plugin.roadsigns.sources.last", null);
if (!Objects.equals(customFile, lastCustomFile)) {
Main.pref.put("plugin.roadsigns.sources.last", customFile);
Main.pref.put("plugin.roadsigns.preset.selection", "custom");
}
String customIconDirsStr = Main.pref.get("plugin.roadsigns.icon.sources", null);
Collection<String> customIconDirs = null;
if (customIconDirsStr != null) {
customIconDirs = new ArrayList<>(Arrays.asList(customIconDirsStr.split(",")));
} else {
customIconDirs = Main.pref.getCollection("plugin.roadsigns.icon.sources", null);
}
if (customIconDirs != null) {
customIconDirs = new ArrayList<>(customIconDirs);
} else {
customIconDirs = new ArrayList<>();
}
// add icon directory relative to preset file
if (!customFile.startsWith("resource:")) {
String parentDir = null;
try {
URL url = new URL(customFile);
parentDir = url.getPath();
} catch (MalformedURLException ex) {
File f = new File(customFile);
parentDir = f.getParent();
}
if (parentDir != null && !parentDir.isEmpty()) {
customIconDirs.add(parentDir);
}
}
if (Main.pref.getBoolean("plugin.roadsigns.use_default_icon_source", true)) {
customIconDirs.add("resource://images/");
}
PresetMetaData custom = new PresetMetaData("custom", tr("custom"), customFile, Utils.join(",", customIconDirs));
presetsData.add(custom);
} else {
Main.pref.put("plugin.roadsigns.sources.last", null);
}
return presetsData;
}
protected static void loadSignPreset() throws IOException {
List<PresetMetaData> presetsData = getAvailablePresetsMetaData();
String code = Main.pref.get("plugin.roadsigns.preset.selection", null);
for (PresetMetaData data : presetsData) {
if (data.code.equals(code)) {
selectedPreset = data;
break;
}
}
if (selectedPreset == null) {
if (!presetsData.isEmpty()) {
selectedPreset = presetsData.get(0);
} else {
selectedPreset = PRESET_DE;
}
}
iconDirs = Arrays.asList(selectedPreset.icon_path.split(","));
String source = selectedPreset.preset_path;
try {
InputStream in = getInputStream(source);
RoadSignsReader reader = new RoadSignsReader(in);
signs = reader.parse();
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(
Main.parent,
tr("Could not read tagging preset source: ''{0}''", source),
tr("Error"),
JOptionPane.ERROR_MESSAGE
);
throw ex;
} catch (SAXException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(
Main.parent,
tr("Error parsing tagging preset from ''{0}'':\n", source)+ex.getMessage(),
tr("Error"),
JOptionPane.ERROR_MESSAGE
);
throw new IOException(ex);
}
}
/**
* Returns an inputstream from urls, files and classloaders, depending on the name.
*/
@SuppressWarnings("resource")
public static InputStream getInputStream(String source) throws IOException {
InputStream in = null;
if (source.startsWith("http://") || source.startsWith("https://") || source.startsWith("ftp://")) {
in = new CachedFile(source).getInputStream();
} else if (source.startsWith("file:")) {
in = new URL(source).openStream();
} else if (source.startsWith("resource://")) {
in = RoadSignsPlugin.class.getResourceAsStream(source.substring("resource:/".length()));
} else {
in = new FileInputStream(source);
}
return in;
}
}