package org.openstreetmap.josm.plugins.housenumbertool;
import static org.openstreetmap.josm.tools.I18n.tr;
import static org.openstreetmap.josm.tools.I18n.trn;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.command.ChangePropertyCommand;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.command.SequenceCommand;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.gui.ExtendedDialog;
import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingComboBox;
import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionListItem;
import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
/**
* @author Oliver Raupach 09.01.2012 <http://www.oliver-raupach.de>
* @author Victor Kropp 10.03.2012 <http://victor.kropp.name>
*/
public class TagDialog extends ExtendedDialog {
private static final String APPLY_CHANGES = tr("Apply Changes");
private static final String TAG_STREET_OR_PLACE = tr("Use tag ''addr:street'' or ''addr:place''");
private static final String TAG_BUILDING = "building";
private static final String TAG_ADDR_COUNTRY = "addr:country";
private static final String TAG_ADDR_STATE = "addr:state";
private static final String TAG_ADDR_CITY = "addr:city";
private static final String TAG_ADDR_POSTCODE = "addr:postcode";
private static final String TAG_ADDR_HOUSENUMBER = "addr:housenumber";
private static final String TAG_ADDR_STREET = "addr:street";
private static final String TAG_ADDR_PLACE = "addr:place";
private static final String[] BUILDING_STRINGS = {
"yes", "apartments", "chapel", "church", "commercial", "dormitory", "hotel", "house", "residential", "terrace",
"industrial", "retail", "warehouse", "cathedral", "civic", "hospital", "school", "train_station", "transportation",
"university", "public", "bridge", "bunker", "cabin", "construction", "farm_auxiliary", "garage", "garages",
"greenhouse", "hangar", "hut", "roof", "shed", "stable"};
private static final int FPS_MIN = -2;
private static final int FPS_MAX = 2;
private static final Logger LOGGER = Logger.getLogger(TagDialog.class.getName());
private String pluginDir;
private AutoCompletionManager acm;
private OsmPrimitive selection;
private static final String TEMPLATE_DATA = "/template.data";
private AutoCompletingComboBox country;
private AutoCompletingComboBox state;
private AutoCompletingComboBox city;
private AutoCompletingComboBox postcode;
private AutoCompletingComboBox street;
private JTextField housnumber;
private JCheckBox buildingEnabled;
private JCheckBox countryEnabled;
private JCheckBox stateEnabled;
private JCheckBox cityEnabled;
private JCheckBox zipEnabled;
private JCheckBox streetEnabled;
private JCheckBox housenumberEnabled;
private JSlider housenumberChangeSequence;
private JComboBox<String> building;
private JRadioButton streetRadio;
private JRadioButton placeRadio;
public TagDialog(String pluginDir, OsmPrimitive selection) {
super(Main.parent, tr("House Number Editor"), new String[] { tr("OK"), tr("Cancel") }, true);
this.pluginDir = pluginDir;
this.selection = selection;
JPanel editPanel = createContentPane();
setContent(editPanel);
setButtonIcons(new String[] { "ok.png", "cancel.png" });
setDefaultButton(1);
setupDialog();
getRootPane().setDefaultButton(defaultButton);
// middle of the screen
setLocationRelativeTo(null);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
housnumber.requestFocus();
housnumber.selectAll();
}
});
}
private JPanel createContentPane() {
acm = selection.getDataSet().getAutoCompletionManager();
Dto dto = loadDto();
JPanel editPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
buildingEnabled = new JCheckBox(TAG_BUILDING);
buildingEnabled.setFocusable(false);
buildingEnabled.setSelected(dto.isSaveBuilding());
buildingEnabled.setToolTipText(APPLY_CHANGES);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(buildingEnabled, c);
Arrays.sort(BUILDING_STRINGS);
building = new JComboBox<>(BUILDING_STRINGS);
building.setSelectedItem(dto.getBuilding());
building.setMaximumRowCount(50);
c.gridx = 3;
c.gridy = 0;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(building, c);
// country
countryEnabled = new JCheckBox(TAG_ADDR_COUNTRY);
countryEnabled.setFocusable(false);
countryEnabled.setSelected(dto.isSaveCountry());
countryEnabled.setToolTipText(APPLY_CHANGES);
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 1;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(countryEnabled, c);
country = new AutoCompletingComboBox();
country.setPossibleACItems(acm.getValues(TAG_ADDR_COUNTRY));
country.setPreferredSize(new Dimension(200, 24));
country.setEditable(true);
country.setSelectedItem(dto.getCountry());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 3;
c.gridy = 1;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(country, c);
// state
stateEnabled = new JCheckBox(TAG_ADDR_STATE);
stateEnabled.setFocusable(false);
stateEnabled.setSelected(dto.isSaveState());
stateEnabled.setToolTipText(APPLY_CHANGES);
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 2;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(stateEnabled, c);
state = new AutoCompletingComboBox();
state.setPossibleACItems(acm.getValues(TAG_ADDR_STATE));
state.setPreferredSize(new Dimension(200, 24));
state.setEditable(true);
state.setSelectedItem(dto.getState());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 3;
c.gridy = 2;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(state, c);
// city
cityEnabled = new JCheckBox(TAG_ADDR_CITY);
cityEnabled.setFocusable(false);
cityEnabled.setSelected(dto.isSaveCity());
cityEnabled.setToolTipText(APPLY_CHANGES);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 3;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(cityEnabled, c);
city = new AutoCompletingComboBox();
city.setPossibleACItems(acm.getValues(TAG_ADDR_CITY));
city.setPreferredSize(new Dimension(200, 24));
city.setEditable(true);
city.setSelectedItem(dto.getCity());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 3;
c.gridy = 3;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(city, c);
// postcode
zipEnabled = new JCheckBox(TAG_ADDR_POSTCODE);
zipEnabled.setFocusable(false);
zipEnabled.setSelected(dto.isSavePostcode());
zipEnabled.setToolTipText(APPLY_CHANGES);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 4;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(zipEnabled, c);
postcode = new AutoCompletingComboBox();
postcode.setPossibleACItems(acm.getValues(TAG_ADDR_POSTCODE));
postcode.setPreferredSize(new Dimension(200, 24));
postcode.setEditable(true);
postcode.setSelectedItem(dto.getPostcode());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 3;
c.gridy = 4;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(postcode, c);
// street
streetEnabled = new JCheckBox();
streetEnabled.setFocusable(false);
streetEnabled.setSelected(dto.isSaveStreet());
streetEnabled.setToolTipText(APPLY_CHANGES);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 5;
c.weightx = 0;
c.gridwidth = 1;
editPanel.add(streetEnabled, c);
streetRadio = new JRadioButton(TAG_ADDR_STREET);
streetRadio.setToolTipText(TAG_STREET_OR_PLACE);
streetRadio.setSelected(dto.isTagStreet());
streetRadio.addItemListener(new RadioChangeListener());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 5;
c.weightx = 0;
c.gridwidth = 1;
editPanel.add(streetRadio, c);
placeRadio = new JRadioButton("addr:place");
placeRadio.setToolTipText(TAG_STREET_OR_PLACE);
placeRadio.setSelected(!dto.isTagStreet());
placeRadio.addItemListener(new RadioChangeListener());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 5;
c.weightx = 0;
c.gridwidth = 1;
editPanel.add(placeRadio, c);
ButtonGroup g = new ButtonGroup();
g.add( streetRadio );
g.add( placeRadio );
street = new AutoCompletingComboBox();
if (dto.isTagStreet()) {
street.setPossibleItems(getPossibleStreets());
} else {
street.setPossibleACItems(acm.getValues(TAG_ADDR_PLACE));
}
street.setPreferredSize(new Dimension(200, 24));
street.setEditable(true);
street.setSelectedItem(dto.getStreet());
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 3;
c.gridy = 5;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(street, c);
// housenumber
housenumberEnabled = new JCheckBox(TAG_ADDR_HOUSENUMBER);
housenumberEnabled.setFocusable(false);
housenumberEnabled.setSelected(dto.isSaveHousenumber());
housenumberEnabled.setToolTipText(APPLY_CHANGES);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 6;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(housenumberEnabled, c);
housnumber = new JTextField();
housnumber.setPreferredSize(new Dimension(200, 24));
int number = 0;
try {
number = Integer.valueOf(dto.getHousenumber()) + dto.getHousenumberChangeValue();
} catch (NumberFormatException e) {
// Do nothing
}
if (number > 0) {
housnumber.setText(String.valueOf(number));
}
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 3;
c.gridy = 6;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(housnumber, c);
JLabel seqLabel = new JLabel(tr("House number increment:"));
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 7;
c.weightx = 0;
c.gridwidth = 3;
editPanel.add(seqLabel, c);
housenumberChangeSequence = new JSlider(JSlider.HORIZONTAL, FPS_MIN, FPS_MAX, dto.getHousenumberChangeValue());
housenumberChangeSequence.setPaintTicks(true);
housenumberChangeSequence.setMajorTickSpacing(1);
housenumberChangeSequence.setMinorTickSpacing(1);
housenumberChangeSequence.setPaintLabels(true);
housenumberChangeSequence.setSnapToTicks(true);
c.gridx = 3;
c.gridy = 7;
c.weightx = 1;
c.gridwidth = 1;
editPanel.add(housenumberChangeSequence, c);
return editPanel;
}
@Override
protected void buttonAction(int buttonIndex, ActionEvent evt) {
if (buttonIndex == 0) {
Dto dto = new Dto();
dto.setSaveBuilding(buildingEnabled.isSelected());
dto.setSaveCity(cityEnabled.isSelected());
dto.setSaveCountry(countryEnabled.isSelected());
dto.setSaveState(stateEnabled.isSelected());
dto.setSaveHousenumber(housenumberEnabled.isSelected());
dto.setSavePostcode(zipEnabled.isSelected());
dto.setSaveStreet(streetEnabled.isSelected());
dto.setTagStreet(streetRadio.isSelected());
dto.setBuilding((String) building.getSelectedItem());
dto.setCity(getAutoCompletingComboBoxValue(city));
dto.setCountry(getAutoCompletingComboBoxValue(country));
dto.setHousenumber(housnumber.getText());
dto.setPostcode(getAutoCompletingComboBoxValue(postcode));
dto.setStreet(getAutoCompletingComboBoxValue(street));
dto.setState(getAutoCompletingComboBoxValue(state));
dto.setHousenumberChangeValue(housenumberChangeSequence.getValue());
updateJOSMSelection(selection, dto);
saveDto(dto);
}
setVisible(false);
}
private String getAutoCompletingComboBoxValue(AutoCompletingComboBox box) {
Object item = box.getSelectedItem();
if (item != null) {
if (item instanceof String) {
return (String) item;
}
if (item instanceof AutoCompletionListItem) {
return ((AutoCompletionListItem) item).getValue();
}
return item.toString();
} else {
return "";
}
}
protected void saveDto(Dto dto) {
File path = new File(pluginDir);
File fileName = new File(pluginDir + TagDialog.TEMPLATE_DATA);
try {
path.mkdirs();
try (
FileOutputStream file = new FileOutputStream(fileName);
ObjectOutputStream o = new ObjectOutputStream(file)
) {
o.writeObject(dto);
}
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, ex.getMessage());
fileName.delete();
}
}
protected void updateJOSMSelection(OsmPrimitive selection, Dto dto) {
List<Command> commands = new ArrayList<>();
if (dto.isSaveBuilding()) {
String value = selection.get(TagDialog.TAG_BUILDING);
if (value == null || (value != null && !value.equals(dto.getBuilding()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_BUILDING, dto.getBuilding());
commands.add(command);
}
}
if (dto.isSaveCity()) {
String value = selection.get(TagDialog.TAG_ADDR_CITY);
if (value == null || (value != null && !value.equals(dto.getCity()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_CITY, dto.getCity());
commands.add(command);
}
}
if (dto.isSaveCountry()) {
String value = selection.get(TagDialog.TAG_ADDR_COUNTRY);
if (value == null || (value != null && !value.equals(dto.getCountry()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_COUNTRY, dto.getCountry());
commands.add(command);
}
}
if (dto.isSaveHousenumber()) {
String value = selection.get(TagDialog.TAG_ADDR_HOUSENUMBER);
if (value == null || (value != null && !value.equals(dto.getHousenumber()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_HOUSENUMBER, dto.getHousenumber());
commands.add(command);
}
}
if (dto.isSavePostcode()) {
String value = selection.get(TagDialog.TAG_ADDR_POSTCODE);
if (value == null || (value != null && !value.equals(dto.getPostcode()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_POSTCODE, dto.getPostcode());
commands.add(command);
}
}
if (dto.isSaveStreet()) {
if (dto.isTagStreet()) {
String value = selection.get(TagDialog.TAG_ADDR_STREET);
if (value == null || (value != null && !value.equals(dto.getStreet()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_STREET, dto.getStreet());
commands.add(command);
// remove old place-tag
if (selection.get(TagDialog.TAG_ADDR_PLACE) != null) {
ChangePropertyCommand command2 = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_PLACE, null);
commands.add(command2);
}
}
} else {
String value = selection.get(TagDialog.TAG_ADDR_PLACE);
if (value == null || (value != null && !value.equals(dto.getStreet()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_PLACE, dto.getStreet());
commands.add(command);
// remove old place-tag
if (selection.get(TagDialog.TAG_ADDR_STREET) != null) {
ChangePropertyCommand command2 = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_STREET, null);
commands.add(command2);
}
}
}
}
if (dto.isSaveState()) {
String value = selection.get(TagDialog.TAG_ADDR_STATE);
if (value == null || (value != null && !value.equals(dto.getState()))) {
ChangePropertyCommand command = new ChangePropertyCommand(selection, TagDialog.TAG_ADDR_STATE, dto.getState());
commands.add(command);
}
}
if (!commands.isEmpty()) {
SequenceCommand sequenceCommand = new SequenceCommand(
trn("Updating properties of up to {0} object",
"Updating properties of up to {0} objects", commands.size(), commands.size()), commands);
// executes the commands and adds them to the undo/redo chains
Main.main.undoRedo.add(sequenceCommand);
}
}
private Collection<String> getPossibleStreets() {
/**
* Generates a list of all visible names of highways in order to do autocompletion on the road name.
*/
Set<String> names = new TreeSet<>();
for (OsmPrimitive osm : Main.getLayerManager().getEditDataSet().allNonDeletedPrimitives()) {
if (osm.getKeys() != null && osm.keySet().contains("highway") && osm.keySet().contains("name")) {
names.add(osm.get("name"));
}
}
return names;
}
private Dto loadDto() {
Dto dto = new Dto();
File fileName = new File(pluginDir + TagDialog.TEMPLATE_DATA);
try {
if (fileName.exists()) {
try (
FileInputStream file = new FileInputStream(fileName);
ObjectInputStream o = new ObjectInputStream(file);
) {
dto = (Dto) o.readObject();
}
} else {
dto.setCity(selection.get(TagDialog.TAG_ADDR_CITY));
dto.setCountry(selection.get(TagDialog.TAG_ADDR_COUNTRY));
dto.setHousenumber(selection.get(TagDialog.TAG_ADDR_HOUSENUMBER));
dto.setPostcode(selection.get(TagDialog.TAG_ADDR_POSTCODE));
dto.setStreet(selection.get(TagDialog.TAG_ADDR_STREET));
dto.setState(selection.get(TagDialog.TAG_ADDR_STATE));
}
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, ex.getMessage());
fileName.delete();
}
return dto;
}
class RadioChangeListener implements ItemListener {
@Override
public void itemStateChanged(ItemEvent e) {
if (streetRadio.isSelected()) {
street.setPossibleItems(getPossibleStreets());
} else {
street.setPossibleACItems(acm.getValues(TAG_ADDR_PLACE));
}
}
}
}