/*
* Indoorhelper is a JOSM plug-in to support users when creating their own indoor maps.
* Copyright (C) 2016 Erik Gruschka
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package controller;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JOptionPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.ValidateAction;
import org.openstreetmap.josm.data.osm.Tag;
import org.openstreetmap.josm.data.preferences.MapListSetting;
import org.openstreetmap.josm.data.preferences.Setting;
import org.openstreetmap.josm.data.validation.OsmValidator;
import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
import org.openstreetmap.josm.gui.dialogs.FilterDialog;
import org.openstreetmap.josm.gui.dialogs.FilterTableModel;
import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
import model.IndoorHelperModel;
import model.TagCatalog.IndoorObject;
import views.FittingView;
import views.LevelSelectorView;
import views.ToolBoxView;
/**
*
* Class for the Controller which provides the communication between
* the IndoorHelperModel and the different views.
*
* @author egru
*
*/
public class IndoorHelperController {
private IndoorHelperModel model;
private ToolBoxView toolboxView;
private FittingView fittingView;
private LevelSelectorView selectorView;
private String sep = System.getProperty("file.separator");
private int lastLevelIndex;
/**
* Constructor for the {@link IndoorHelperController} which initiates model and views.
*
*/
public IndoorHelperController() {
this.model = new IndoorHelperModel();
this.toolboxView = new ToolBoxView();
this.lastLevelIndex = 0;
addToolboxListeners();
Main.map.addToggleDialog(toolboxView);
}
/**
* Adds the button- and box-listeners to the {@link ToolBoxView}.
*/
private void addToolboxListeners() {
if (this.toolboxView != null) {
this.toolboxView.setPowerButtonListener(new ToolPowerButtonListener());
this.toolboxView.setApplyButtonListener(new ToolApplyButtonListener());
this.toolboxView.setLevelItemListener(new ToolLevelItemListener());
this.toolboxView.setObjectItemListener(new ToolObjectItemListener());
this.toolboxView.setPreset1Listener(new Preset1Listener());
this.toolboxView.setPreset2Listener(new Preset2Listener());
this.toolboxView.setPreset3Listener(new Preset3Listener());
this.toolboxView.setPreset4Listener(new Preset4Listener());
}
}
/**
* Adds the button-listeners to the {@link LevelSelectorView}.
*/
private void addLevelSelectorListeners() {
if (this.selectorView != null) {
this.selectorView.setOkButtonListener(new LevelOkButtonListener());
this.selectorView.setCancelButtonListener(new LevelCancelButtonListener());
}
}
/**
* Adds the button-listeners to the {@link FittingView}.
*/
private void addFittingListeners() {
if (this.fittingView != null) {
this.fittingView.setOkButtonListener(new FittingOkButtonListener());
}
}
//********************************************************************
//********************* TOOLBOX LISTENERS ************************
//********************************************************************
/**
* The listener which handles the power button.
*
* @author egru
*
*/
class ToolPowerButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (toolboxView.getPowerButtonState()) {
selectorView = new LevelSelectorView();
addLevelSelectorListeners();
selectorView.setVisible(true);
setPluginPreferences(true);
} else if (!toolboxView.getPowerButtonState()) {
model = new IndoorHelperModel();
selectorView.dispose();
toolboxView.reset();
setPluginPreferences(false);
// Delete the indoor filters
FilterDialog filterDialog = Main.map.getToggleDialog(FilterDialog.class);
if (filterDialog != null) {
FilterTableModel filterTableModel = filterDialog.getFilterModel();
for (int i = filterTableModel.getRowCount()-1; i > -1; i--) {
if (filterTableModel.getFilter(i).text.startsWith("\"indoor:level\"=\"")) {
filterTableModel.removeFilter(i);
}
}
}
}
}
}
/**
* The listener which provides the handling of the apply button.
* Gets the texts which were written by the user and writes them to the OSM-data.
* After that it checks the tagged data with the built-in validator file.
*
* @author egru
*/
class ToolApplyButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
IndoorObject indoorObject = toolboxView.getSelectedObject();
if (toolboxView.getNameText().isEmpty() && toolboxView.getRefText().isEmpty() && toolboxView.getLevelName().isEmpty()) {
model.addTagsToOSM(indoorObject);
} else {
List<Tag> tags = new ArrayList<>();
if (!toolboxView.getLevelName().isEmpty()) {
model.getLevelList().get(toolboxView.getSelectedLevelIndex()).setNameTag(toolboxView.getLevelName());
}
if (!toolboxView.getNameText().isEmpty()) {
tags.add(new Tag("name", toolboxView.getNameText()));
}
if (!toolboxView.getRefText().isEmpty()) {
tags.add(new Tag("ref", toolboxView.getRefText()));
}
model.addTagsToOSM(indoorObject, tags);
}
//Do the validation process
ValidateAction validateAction = new ValidateAction();
validateAction.doValidate(true);
refreshPresets();
}
}
/**
* <pre>The listener which is called when a new item in the level list is selected.
*It also sets the name-tag for a level, if the user has done an input in the textbox.
* </pre>
* @author egru
*
*/
class ToolLevelItemListener implements ItemListener {
@Override
public void itemStateChanged(ItemEvent e) {
if (!toolboxView.levelListIsEmpty()) {
if (!toolboxView.getLevelName().isEmpty()) {
model.getLevelList().get(lastLevelIndex).setNameTag(toolboxView.getLevelName());
}
if (!model.getLevelList().get(toolboxView.getSelectedLevelIndex()).hasEmptyName()) {
toolboxView.setLevelName(model.getLevelList().get(toolboxView.getSelectedLevelIndex()).getName());
} else {
toolboxView.setLevelName("");
}
model.setWorkingLevel(toolboxView.getSelectedLevelIndex());
lastLevelIndex = toolboxView.getSelectedLevelIndex();
}
}
}
/**
* The listener which is called when a new item in the object list is selected.
*
* @author egru
*
*/
class ToolObjectItemListener implements ItemListener {
@Override
public void itemStateChanged(ItemEvent e) {
if (toolboxView.getSelectedObject().equals(IndoorObject.ROOM)) {
toolboxView.setTagUiElementsEnabled(true);
} else {
toolboxView.setTagUiElementsEnabled(false);
}
}
}
/**
* Listener for preset button 1.
* @author egru
*
*/
class Preset1Listener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
model.addTagsToOSM(toolboxView.getPreset1());
}
}
/**
* Listener for preset button 2.
* @author egru
*
*/
class Preset2Listener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
model.addTagsToOSM(toolboxView.getPreset2());
}
}
/**
* Listener for preset button 3.
* @author egru
*
*/
class Preset3Listener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
model.addTagsToOSM(toolboxView.getPreset3());
}
}
/**
* Listener for preset button 4.
* @author egru
*
*/
class Preset4Listener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
model.addTagsToOSM(toolboxView.getPreset4());
}
}
/**
* Updates the preset button from the current ranking.
*/
private void refreshPresets() {
toolboxView.setPresetButtons(model.getPresetRanking());
}
//*******************
// SELECTOR LISTENERS
//*******************
/**
* <pre>
* The listener which handles the click on the OK-button of the {@link LevelSelectorView}.
* It sends the data of the view to the model and displays an error message,
* if the level-list couldn't be created.
* </pre>
* @author egru
*
*/
class LevelOkButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
boolean levelSuccess = model.setBuildingLevels(selectorView.getMin(), selectorView.getMax());
if (levelSuccess) {
toolboxView.setLevelList(model.getLevelList()); //set the levels to the ComboBox and
model.setWorkingLevel(toolboxView.getSelectedLevelIndex()); //sets the working level in the model
selectorView.dispose();
fittingView = new FittingView();
addFittingListeners();
fittingView.setVisible(true);
} else {
JOptionPane.showMessageDialog(null, "Lowest Level has to be lower than the highest level",
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* Closes the level selection view if the user hits the cancel button.
*
* @author egru
*
*/
class LevelCancelButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
selectorView.dispose();
toolboxView.setPowerButtonDisabled();
setPluginPreferences(false);
}
}
//*******************
// FITTING LISTENERS
//*******************
/**
* Closes the {@link FittingView} if the OK-Button is clicked.
* Enables the UI elements of the toolbox
*
* @author egru
*
*/
class FittingOkButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
fittingView.dispose();
toolboxView.setAllUiElementsEnabled(true);
toolboxView.setTagUiElementsEnabled(false);
}
}
/*
HELPER METHODS
*/
/**
* Enables or disables the preferences for the mapcss-style and the validator.
*
* @param enabled Activates or disables the settings.
*/
private void setPluginPreferences(boolean enabled) {
Map<String, Setting<?>> settings = Main.pref.getAllSettings();
MapListSetting validatorMapListSetting = (MapListSetting) settings.
get("validator.org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.entries");
List<Map<String, String>> validatorMaps = new ArrayList<>();
if (validatorMapListSetting != null) {
validatorMaps = validatorMapListSetting.getValue();
}
MapListSetting styleMapListSetting = (MapListSetting) settings.
get("mappaint.style.entries");
List<Map<String, String>> styleMaps = new ArrayList<>();
if (styleMapListSetting != null) {
styleMaps = styleMapListSetting.getValue();
}
if (enabled) {
//set the validator active
List<Map<String, String>> validatorMapsNew = new ArrayList<>();
if (!validatorMaps.isEmpty()) {
validatorMapsNew.addAll(validatorMaps);
}
for (Map<String, String> map : validatorMapsNew) {
if (map.containsValue("Indoor")) {
validatorMapsNew.remove(map);
break;
}
}
Map<String, String> indoorValidator = new HashMap<>();
indoorValidator.put("title", "Indoor");
indoorValidator.put("active", "true");
indoorValidator.put("url", Main.pref.getUserDataDirectory()+ sep +"validator" +
sep + "indoorhelper.validator.mapcss");
validatorMapsNew.add(indoorValidator);
Main.pref.putListOfStructs("validator.org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.entries",
validatorMapsNew);
//set mappaint active
List<Map<String, String>> styleMapsNew = new ArrayList<>();
if (!styleMaps.isEmpty()) {
styleMapsNew.addAll(styleMaps);
}
for (Map<String, String> map : styleMapsNew) {
if (map.containsValue("Indoor")) {
styleMapsNew.remove(map);
break;
}
}
Map<String, String> indoorMapPaint = new HashMap<>();
indoorMapPaint.put("title", "Indoor");
indoorMapPaint.put("active", "true");
indoorMapPaint.put("url", Main.pref.getUserDataDirectory() + sep + "styles"
+ sep + "indoor.mapcss");
styleMapsNew.add(indoorMapPaint);
Main.pref.putListOfStructs("mappaint.style.entries", styleMapsNew);
updateSettings();
} else {
//set the validator inactive
List<Map<String, String>> validatorMapsNew = new ArrayList<>();
if (!validatorMaps.isEmpty()) {
validatorMapsNew.addAll(validatorMaps);
}
for (Map<String, String> map : validatorMapsNew) {
if (map.containsValue("Indoor")) {
validatorMapsNew.remove(map);
break;
}
}
Map<String, String> indoorValidator = new HashMap<>();
indoorValidator.put("title", "Indoor");
indoorValidator.put("active", "false");
indoorValidator.put("url", Main.pref.getUserDataDirectory()+ sep +"validator" +
sep + "indoorhelper.validator.mapcss");
validatorMapsNew.add(indoorValidator);
Main.pref.putListOfStructs("validator.org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.entries",
validatorMapsNew);
//set mappaint inactive
List<Map<String, String>> styleMapsNew = new ArrayList<>();
if (!styleMaps.isEmpty()) {
styleMapsNew.addAll(styleMaps);
}
for (Map<String, String> map : styleMapsNew) {
if (map.containsValue("Indoor")) {
styleMapsNew.remove(map);
break;
}
}
Map<String, String> indoorMapPaint = new HashMap<>();
indoorMapPaint.put("title", "Indoor");
indoorMapPaint.put("active", "false");
indoorMapPaint.put("url", Main.pref.getUserDataDirectory() + sep + "styles"
+ sep + "indoor.mapcss");
styleMapsNew.add(indoorMapPaint);
Main.pref.putListOfStructs("mappaint.style.entries", styleMapsNew);
updateSettings();
}
}
/**
* Forces JOSM to load the validator and mappaint settings.
*/
private void updateSettings() {
Main.pref.init(false);
MapCSSTagChecker tagChecker = OsmValidator.getTest(MapCSSTagChecker.class);
if (tagChecker != null) {
OsmValidator.initializeTests(Collections.singleton(tagChecker));
}
MapPaintStyles.readFromPreferences();
}
}