// License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.actions;
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
import static org.openstreetmap.josm.tools.I18n.marktr;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import javax.swing.JOptionPane;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.Bounds;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
import org.openstreetmap.josm.gui.MapView;
import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.tools.Shortcut;
/**
* Toggles the autoScale feature of the mapView
* @author imi
*/
public class AutoScaleAction extends JosmAction {
public static final String[] MODES = {
marktr("data"),
marktr("layer"),
marktr("selection"),
marktr("conflict"),
marktr("download"),
marktr("previous"),
marktr("next")};
/**
* Zooms the current map view to the currently selected primitives.
* Does nothing if there either isn't a current map view or if there isn't a current data
* layer.
*
*/
public static void zoomToSelection() {
if (Main.main == null || Main.main.getEditLayer() == null) return;
if (Main.map == null || Main.map.mapView == null) return;
Collection<OsmPrimitive> sel = Main.main.getEditLayer().data.getSelected();
if (sel.isEmpty()) {
JOptionPane.showMessageDialog(
Main.parent,
tr("Nothing selected to zoom to."),
tr("Information"),
JOptionPane.INFORMATION_MESSAGE
);
return;
}
BoundingXYVisitor bboxCalculator = new BoundingXYVisitor();
bboxCalculator.computeBoundingBox(sel);
// increase bbox by 0.001 degrees on each side. this is required
// especially if the bbox contains one single node, but helpful
// in most other cases as well.
bboxCalculator.enlargeBoundingBox();
if (bboxCalculator.getBounds() != null) {
Main.map.mapView.recalculateCenterScale(bboxCalculator);
}
}
private final String mode;
private static int getModeShortcut(String mode) {
int shortcut = -1;
if (mode.equals("data")) {
shortcut = KeyEvent.VK_1;
}
if (mode.equals("layer")) {
shortcut = KeyEvent.VK_2;
}
if (mode.equals("selection")) {
shortcut = KeyEvent.VK_3;
}
if (mode.equals("conflict")) {
shortcut = KeyEvent.VK_4;
}
if (mode.equals("download")) {
shortcut = KeyEvent.VK_5;
}
if (mode.equals("previous")) {
shortcut = KeyEvent.VK_8;
}
if (mode.equals("next")) {
shortcut = KeyEvent.VK_9;
}
return shortcut;
}
public AutoScaleAction(String mode) {
super(tr("Zoom to {0}", tr(mode)), "dialogs/autoscale/" + mode, tr("Zoom the view to {0}.", tr(mode)),
Shortcut.registerShortcut("view:zoom"+mode, tr("View: {0}", tr("Zoom to {0}", tr(mode))), getModeShortcut(mode), Shortcut.GROUP_EDIT), true);
String modeHelp = Character.toUpperCase(mode.charAt(0)) + mode.substring(1);
putValue("help", "Action/AutoScale/" + modeHelp);
this.mode = mode;
if (mode.equals("data")) {
putValue("help", ht("/Action/ZoomToData"));
} else if (mode.equals("layer")) {
putValue("help", ht("/Action/ZoomToLayer"));
} else if (mode.equals("selection")) {
putValue("help", ht("/Action/ZoomToSelection"));
} else if (mode.equals("conflict")) {
putValue("help", ht("/Action/ZoomToConflict"));
} else if (mode.equals("download")) {
putValue("help", ht("/Action/ZoomToDownload"));
} else if (mode.equals("previous")) {
putValue("help", ht("/Action/ZoomPrevious"));
} else if (mode.equals("next")) {
putValue("help", ht("/Action/ZoomNext"));
}
}
public void autoScale() {
if (Main.map != null) {
if (mode.equals("previous")) {
Main.map.mapView.zoomPrevious();
} else if (mode.equals("next")) {
Main.map.mapView.zoomNext();
} else {
BoundingXYVisitor bbox = getBoundingBox();
if (bbox != null && bbox.getBounds() != null) {
Main.map.mapView.recalculateCenterScale(bbox);
}
}
}
putValue("active", true);
}
public void actionPerformed(ActionEvent e) {
autoScale();
}
protected Layer getActiveLayer() {
try {
return Main.map.mapView.getActiveLayer();
} catch(NullPointerException e) {
return null;
}
}
/**
* Replies the first selected layer in the layer list dialog. null, if no
* such layer exists, either because the layer list dialog is not yet created
* or because no layer is selected.
*
* @return the first selected layer in the layer list dialog
*/
protected Layer getFirstSelectedLayer() {
if (LayerListDialog.getInstance() == null) return null;
List<Layer> layers = LayerListDialog.getInstance().getModel().getSelectedLayers();
if (layers.isEmpty()) return null;
return layers.get(0);
}
private BoundingXYVisitor getBoundingBox() {
BoundingXYVisitor v = new BoundingXYVisitor();
if (mode.equals("data")) {
for (Layer l : Main.map.mapView.getAllLayers()) {
l.visitBoundingBox(v);
}
} else if (mode.equals("layer")) {
if (getActiveLayer() == null)
return null;
// try to zoom to the first selected layer
//
Layer l = getFirstSelectedLayer();
if (l == null) return null;
l.visitBoundingBox(v);
} else if (mode.equals("selection") || mode.equals("conflict")) {
Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
if (mode.equals("selection")) {
sel = getCurrentDataSet().getSelected();
} else if (mode.equals("conflict")) {
if (Main.map.conflictDialog.getConflicts() != null) {
sel = Main.map.conflictDialog.getConflicts().getMyConflictParties();
}
}
if (sel.isEmpty()) {
JOptionPane.showMessageDialog(
Main.parent,
(mode.equals("selection") ? tr("Nothing selected to zoom to.") : tr("No conflicts to zoom to")),
tr("Information"),
JOptionPane.INFORMATION_MESSAGE
);
return null;
}
for (OsmPrimitive osm : sel) {
osm.visit(v);
}
// increase bbox by 0.001 degrees on each side. this is required
// especially if the bbox contains one single node, but helpful
// in most other cases as well.
v.enlargeBoundingBox();
}
else if (mode.equals("download")) {
if (Main.pref.hasKey("osm-download.bounds")) {
try {
v.visit(new Bounds(Main.pref.get("osm-download.bounds"), ";"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return v;
}
@Override
protected void updateEnabledState() {
if ("selection".equals(mode)) {
setEnabled(getCurrentDataSet() != null && ! getCurrentDataSet().getSelected().isEmpty());
} else if ("layer".equals(mode)) {
if (Main.map == null || Main.map.mapView == null || Main.map.mapView.getAllLayersAsList().isEmpty()) {
setEnabled(false);
} else {
// FIXME: should also check for whether a layer is selected in the layer list dialog
setEnabled(true);
}
} else if ("previous".equals(mode)) {
setEnabled(Main.map != null && Main.map.mapView != null && Main.map.mapView.hasZoomUndoEntries());
} else if ("next".equals(mode)) {
setEnabled(Main.map != null && Main.map.mapView != null && Main.map.mapView.hasZoomRedoEntries());
} else {
setEnabled(
Main.isDisplayingMapView()
&& Main.map.mapView.hasLayers()
);
}
}
@Override
protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
if ("selection".equals(mode)) {
setEnabled(selection != null && !selection.isEmpty());
}
}
@Override
protected void installAdapters() {
super.installAdapters();
// make this action listen to zoom change events
//
zoomChangeAdapter = new ZoomChangeAdapter();
MapView.addZoomChangeListener(zoomChangeAdapter);
initEnabledState();
}
/**
* Adapter for selection change events
*
*/
private class ZoomChangeAdapter implements MapView.ZoomChangeListener {
public void zoomChanged() {
updateEnabledState();
}
}
private ZoomChangeAdapter zoomChangeAdapter;
}