/*******************************************************************************
* Copyright (c) MOBAC developers
*
* 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 2 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 mobac.gui.atlastree;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.dnd.Autoscroll;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import mobac.gui.MainGUI;
import mobac.gui.mapview.PreviewMap;
import mobac.gui.mapview.layer.MapAreaHighlightingLayer;
import mobac.program.interfaces.AtlasInterface;
import mobac.program.interfaces.AtlasObject;
import mobac.program.interfaces.CapabilityDeletable;
import mobac.program.interfaces.LayerInterface;
import mobac.program.interfaces.MapInterface;
import mobac.program.interfaces.RequiresSQLite;
import mobac.program.interfaces.ToolTipProvider;
import mobac.program.model.Atlas;
import mobac.program.model.AtlasOutputFormat;
import mobac.program.model.AtlasTreeModel;
import mobac.program.model.EastNorthCoordinate;
import mobac.program.model.MapSelection;
import mobac.program.model.Profile;
import mobac.program.model.TileImageParameters;
import mobac.utilities.GUIExceptionHandler;
import mobac.utilities.I18nUtils;
import mobac.utilities.jdbc.SQLiteLoader;
import org.apache.log4j.Logger;
public class JAtlasTree extends JTree implements Autoscroll {
private static final long serialVersionUID = 1L;
private static final int margin = 12;
private static final String MSG_ATLAS_VERSION_MISMATCH = I18nUtils.localizedStringForKey("msg_atlas_version_mismatch");
private static final String MSG_ATLAS_DATA_CHECK_FAILED = I18nUtils.localizedStringForKey("msg_atlas_data_check_failed");
private static final String MSG_ATLAS_EMPTY = I18nUtils.localizedStringForKey("msg_atlas_is_empty");
private static final String ACTION_DELETE_NODE = "DELETE_NODE";
private static final Logger log = Logger.getLogger(JAtlasTree.class);
private AtlasTreeModel treeModel;
private PreviewMap mapView;
protected NodeRenderer nodeRenderer;
protected String defaultToolTiptext;
protected KeyStroke deleteNodeKS;
protected DragDropController ddc;
protected boolean displaySelectedMapArea = false;
public JAtlasTree(PreviewMap mapView) {
super(new AtlasTreeModel());
if (mapView == null)
throw new NullPointerException("MapView parameter is null");
this.mapView = mapView;
getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
ddc = new DragDropController(this);
treeModel = (AtlasTreeModel) getModel();
// setRootVisible(false);
setShowsRootHandles(true);
nodeRenderer = new NodeRenderer();
setCellRenderer(nodeRenderer);
setCellEditor(new NodeEditor(this));
setToolTipText("");
defaultToolTiptext = I18nUtils.localizedStringForKey("lp_atlas_default_tip");
setAutoscrolls(true);
addMouseListener(new MouseController(this));
InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
// map moving
inputMap.put(deleteNodeKS = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), ACTION_DELETE_NODE);
actionMap.put(ACTION_DELETE_NODE, new AbstractAction(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_delete_node")) {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
deleteSelectedNode();
JAtlasTree.this.mapView.repaint();
}
});
}
public boolean testAtlasContentValid() {
AtlasInterface atlas = getAtlas();
if (RequiresSQLite.class.isAssignableFrom(atlas.getOutputFormat().getMapCreatorClass())) {
if (!SQLiteLoader.loadSQLiteOrShowError())
return false;
}
if (atlas.calculateTilesToDownload() == 0) {
JOptionPane.showMessageDialog(null, "<html>" + MSG_ATLAS_EMPTY + "</html>", "Error - atlas has no content",
JOptionPane.ERROR_MESSAGE);
return false;
}
return true;
}
@Override
public String getToolTipText(MouseEvent event) {
if (getRowForLocation(event.getX(), event.getY()) == -1)
return defaultToolTiptext;
TreePath curPath = getPathForLocation(event.getX(), event.getY());
Object o = curPath.getLastPathComponent();
if (o == null || !(o instanceof ToolTipProvider))
return null;
return ((ToolTipProvider) o).getToolTip();
}
@Override
public boolean isPathEditable(TreePath path) {
return super.isPathEditable(path) && (path.getLastPathComponent() instanceof AtlasObject);
}
public AtlasTreeModel getTreeModel() {
return treeModel;
}
public void newAtlas(String name, AtlasOutputFormat format) {
log.debug("Creating new atlas");
Atlas newAtlas = Atlas.newInstance();
newAtlas.setOutputFormat(format);
newAtlas.setName(name);
treeModel.setAtlas(newAtlas);
mapView.repaint();
}
public void newAtlas() {
log.debug("Resetting atlas tree model");
Atlas newAtlas = Atlas.newInstance();
newAtlas.setName(MainGUI.getMainGUI().getUserText());
treeModel.setAtlas(newAtlas);
mapView.repaint();
}
/**
* Changes the atlas format
*/
public void convertAtlas(AtlasOutputFormat format) {
log.debug("Converting the atlas format to " + format);
treeModel.getAtlas().setOutputFormat(format);
}
public void deleteSelectedNode() {
TreePath path = getSelectionPath();
if (path == null)
return;
TreeNode selected = (TreeNode) path.getLastPathComponent();
int[] selectedRows = getSelectionRows();
if (!(selected instanceof CapabilityDeletable))
return;
treeModel.notifyNodeDelete(selected);
((CapabilityDeletable) selected).delete();
int selRow = Math.min(selectedRows[0], getRowCount() - 1);
TreePath path1 = path.getParentPath();
TreePath path2 = getPathForRow(selRow).getParentPath();
if (path1 != path2) {
// next row belongs to different parent node -> we select parent
// node instead
setSelectionPath(path1);
} else {
setSelectionRow(selRow);
scrollRowToVisible(selRow);
}
}
public AtlasInterface getAtlas() {
return treeModel.getAtlas();
}
public boolean load(Profile profile) {
log.debug("Loading profile " + profile);
try {
treeModel.load(profile);
if (treeModel.getAtlas() instanceof Atlas) {
Atlas atlas = (Atlas) treeModel.getAtlas();
if (atlas.getVersion() < Atlas.CURRENT_ATLAS_VERSION) {
JOptionPane.showMessageDialog(null, MSG_ATLAS_VERSION_MISMATCH, "Outdated atlas version",
JOptionPane.WARNING_MESSAGE);
return true;
}
}
boolean problemsDetected = Profile.checkAtlas(treeModel.getAtlas());
if (problemsDetected) {
JOptionPane.showMessageDialog(null, MSG_ATLAS_DATA_CHECK_FAILED, "Atlas loading problem",
JOptionPane.WARNING_MESSAGE);
}
return true;
} catch (Exception e) {
GUIExceptionHandler.processException(e);
return false;
}
}
public boolean save(Profile profile) {
try {
treeModel.save(profile);
return true;
} catch (Exception e) {
GUIExceptionHandler.processException(e);
return false;
}
}
protected void showNodePopupMenu(MouseEvent event) {
JPopupMenu pm = new JPopupMenu();
final TreePath selPath = getPathForLocation(event.getX(), event.getY());
setSelectionPath(selPath);
JMenuItem mi = null;
if (selPath != null) {
// not clicked on empty area
final Object o = selPath.getLastPathComponent();
if (o == null)
return;
if (o instanceof ToolTipProvider) {
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_show_detail"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ToolTipProvider ttp = (ToolTipProvider) o;
JOptionPane.showMessageDialog(MainGUI.getMainGUI(), ttp.getToolTip());
}
});
pm.add(mi);
}
if (o instanceof AtlasObject) {
final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_display_select_area"));
final MapAreaHighlightingLayer msl = new MapAreaHighlightingLayer(this);
cbmi.setSelected(displaySelectedMapArea);
cbmi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (displaySelectedMapArea) {
MapAreaHighlightingLayer.removeHighlightingLayers();
} else {
mapView.setSelectionByTileCoordinate(null, null, false);
MapAreaHighlightingLayer.removeHighlightingLayers();
mapView.mapLayers.add(msl);
}
displaySelectedMapArea = !displaySelectedMapArea;
mapView.repaint();
}
});
pm.add(cbmi);
}
if (o instanceof MapInterface) {
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_select_map_box"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MapInterface map = (MapInterface) o;
mapView.setMapSource(map.getMapSource());
mapView.setSelectionByTileCoordinate(map.getZoom(), map.getMinTileCoordinate(), map
.getMaxTileCoordinate(), true);
}
});
pm.add(mi);
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_zoom_to_map_box"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MapInterface map = (MapInterface) o;
MapSelection ms = new MapSelection(map);
mapView.setMapSource(map.getMapSource());
mapView.setSelectionAndZoomTo(ms, true);
mapView.setSelectionByTileCoordinate(map.getZoom(), map.getMinTileCoordinate(), map
.getMaxTileCoordinate(), true);
}
});
pm.add(mi);
}
if (o instanceof LayerInterface) {
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_zoom_to"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
LayerInterface layer = (LayerInterface) o;
EastNorthCoordinate max = new EastNorthCoordinate(Double.NEGATIVE_INFINITY,
Double.NEGATIVE_INFINITY);
EastNorthCoordinate min = new EastNorthCoordinate(Double.POSITIVE_INFINITY,
Double.POSITIVE_INFINITY);
for (MapInterface map : layer) {
MapSelection ms = new MapSelection(map);
EastNorthCoordinate mapMax = ms.getMax();
EastNorthCoordinate mapMin = ms.getMin();
max.lat = Math.max(max.lat, mapMax.lat);
max.lon = Math.max(max.lon, mapMax.lon);
min.lat = Math.min(min.lat, mapMin.lat);
min.lon = Math.min(min.lon, mapMin.lon);
}
MapSelection ms = new MapSelection(mapView.getMapSource(), max, min);
mapView.zoomTo(ms);
}
});
pm.add(mi);
}
if (o instanceof AtlasObject) {
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_rename"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JAtlasTree.this.startEditingAtPath(selPath);
}
});
pm.add(mi);
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_apply_tile_process"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
AtlasObject atlasObject = (AtlasObject) o;
TileImageParameters p = MainGUI.getMainGUI().getSelectedTileImageParameters();
applyTileImageParameters(atlasObject, p);
}
});
pm.add(mi);
}
if (o instanceof CapabilityDeletable) {
pm.addSeparator();
mi = new JMenuItem(getActionMap().get(ACTION_DELETE_NODE));
mi.setAccelerator(deleteNodeKS);
pm.add(mi);
}
}
if (pm.getComponentCount() > 0)
pm.addSeparator();
mi = new JMenuItem(I18nUtils.localizedStringForKey("lp_atlas_pop_menu_clear_atals"));
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
newAtlas();
}
});
pm.add(mi);
pm.show(this, event.getX(), event.getY());
}
protected void applyTileImageParameters(Object o, TileImageParameters p) {
if (o instanceof Iterable<?>) {
Iterable<?> it = (Iterable<?>) o;
for (Object ao : it) {
applyTileImageParameters(ao, p);
}
} else if (o instanceof MapInterface) {
((MapInterface) o).setParameters(p);
}
}
protected void selectElementOnMap(Object o) {
if (o instanceof MapInterface) {
MapInterface map = (MapInterface) o;
mapView.setMapSource(map.getMapSource());
mapView.setSelectionByTileCoordinate(map.getZoom(), map.getMinTileCoordinate(), map.getMaxTileCoordinate(),
true);
}
}
public void autoscroll(Point cursorLocn) {
int realrow = getRowForLocation(cursorLocn.x, cursorLocn.y);
Rectangle outer = getBounds();
realrow = (cursorLocn.y + outer.y <= margin ? realrow < 1 ? 0 : realrow - 1
: realrow < getRowCount() - 1 ? realrow + 1 : realrow);
scrollRowToVisible(realrow);
}
public Insets getAutoscrollInsets() {
Rectangle outer = getBounds();
Rectangle inner = getParent().getBounds();
return new Insets(inner.y - outer.y + margin, inner.x - outer.x + margin, outer.height - inner.height - inner.y
+ outer.y + margin, outer.width - inner.width - inner.x + outer.x + margin);
}
}