/*
GNU General Public License
CacheWolf is a software for PocketPC, Win and Linux that
enables paperless caching.
It supports the sites geocaching.com and opencaching.de
Copyright (C) 2006 CacheWolf development team
See http://www.cachewolf.de/ for more information.
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; version 2 of the License.
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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package CacheWolf.navi;
import CacheWolf.MainForm;
import CacheWolf.MainTab;
import CacheWolf.Preferences;
import CacheWolf.controls.ExecutePanel;
import CacheWolf.controls.InfoBox;
import CacheWolf.controls.MyScrollBarPanel;
import CacheWolf.database.BoundingBox;
import CacheWolf.database.CWPoint;
import CacheWolf.database.CacheDB;
import CacheWolf.database.CacheHolder;
import CacheWolf.database.CacheSize;
import CacheWolf.database.CacheTerrDiff;
import CacheWolf.database.CacheType;
import CacheWolf.database.CoordinatePoint;
import CacheWolf.navi.touchControls.ICommandListener;
import CacheWolf.navi.touchControls.MovingMapControls;
import CacheWolf.utils.Common;
import CacheWolf.utils.Metrics;
import CacheWolf.utils.MyLocale;
import CacheWolf.utils.STRreplace;
import ewe.filechooser.FileChooser;
import ewe.filechooser.FileChooserBase;
import ewe.fx.Color;
import ewe.fx.Dimension;
import ewe.fx.Font;
import ewe.fx.FontMetrics;
import ewe.fx.Graphics;
import ewe.fx.Image;
import ewe.fx.Pen;
import ewe.fx.Point;
import ewe.fx.Rect;
import ewe.fx.mImage;
import ewe.graphics.AniImage;
import ewe.graphics.ImageDragContext;
import ewe.graphics.ImageList;
import ewe.graphics.InteractivePanel;
import ewe.sys.Convert;
import ewe.sys.Double;
import ewe.sys.SystemResourceException;
import ewe.sys.Time;
import ewe.sys.Vm;
import ewe.ui.CellConstants;
import ewe.ui.ControlConstants;
import ewe.ui.ControlEvent;
import ewe.ui.Event;
import ewe.ui.EventListener;
import ewe.ui.Form;
import ewe.ui.FormBase;
import ewe.ui.FormEvent;
import ewe.ui.IKeys;
import ewe.ui.KeyEvent;
import ewe.ui.Menu;
import ewe.ui.MenuEvent;
import ewe.ui.MenuItem;
import ewe.ui.PenEvent;
import ewe.ui.WindowConstants;
import ewe.ui.WindowEvent;
import ewe.ui.mList;
import ewe.util.Vector;
/**
* Class to handle a moving map.
*
* additional classes defined in this file :
*
* class MovingMapPanel extends InteractivePanel implements EventListener Class to display the map bitmap and to select another bitmap to display.
*
* class ListBox extends Form Class to display maps to manually choose from
*
* class ArrowsOnMap extends AniImage
*
*/
public final class MovingMap extends Form implements ICommandListener {
public final static int gotFix = 4; // green
public final static int lostFix = 3; // yellow
public final static int noGPSData = 2; // red
public final static int noGPS = 1; // no GPS-Position marker, manually disconnected
public final static int ignoreGPS = -1; // ignore even changes in GPS-signal (eg. from lost fix to gotFix) this is wanted when the map is moved manually
private final static Image imgSelectedCache = new Image("mark_cache.png");
private final static Image imgGoto = new Image("goto_map.png");
private final static int smallTileWidth = 256; //100;
private final static int smallTileHeight = 256; //100;
private CacheDB cacheDB;
private MovingMapPanel mmp;
private MapsList maps;
private Vector symbols;
private TrackOverlay[] TrackOverlays;
private CWPoint TrackOverlaySetCenterTopLeft;
private Vector tracks;
private MapInfoObject currentMap = null;
private boolean running = false;
private MapSymbol gotoPos = null;
private MapImage mapImage1to1;
private ArrowsOnMap directionArrows = new ArrowsOnMap();
private MapSymbol posCircle;
private int posCircleX = 0, posCircleY = 0;
private AniImage posCircleImageHaveSignal;
private AniImage posCircleImageNoSignal;
private AniImage posCircleImageNoGps;
FontMetrics fm;
// local access
private int GpsStatus;
// ignores updateGps-calls if true
private boolean ignoreGps = false;
private boolean autoSelectMap = true;
// only needed to force updateposition to try to load the best map again after OutOfMemoryError after an repeated click on snap-to-gps
private boolean forceMapLoad = true;
private boolean mapHidden = false;
// to avoid multi-threading problems
private boolean inUpdatePosition;
private boolean inLoadMaps = false;
private boolean dontUpdatePos = false;
private boolean zoomingMode = false;
private boolean mapsloaded = false;
private boolean doPaintPosDestLine = true;
private boolean mobileVGA = false;
private double lastDistance = -1;
// the layer for the buttons
private final MovingMapControls controlsLayer;
private float lastHighestResolutionGPSDestScale = -1;
// Needed by updatePosition to decide if a recalculation of map-tiles is needed:
private int lastXPos;
private int lastYPos;
private int lastWidth;
private int lastHeight;
private int lastCompareX = Integer.MAX_VALUE, lastCompareY = Integer.MAX_VALUE;
// Holds areas not filled by currentMap and/or used tiles
private final Vector whiteAreas = new Vector();
private boolean eventOccurred; // not yet implemented, don't know how (check for abort filling white areas)
/**
* Creating the moving map.
*/
public MovingMap() {
symbols = new Vector();
this.cacheDB = MainForm.profile.cacheDB;
if (Preferences.itself().getScreenHeight() <= 640 && Preferences.itself().getScreenWidth() <= 640) {
this.windowFlagsToSet = WindowConstants.FLAG_FULL_SCREEN;
}
// The following line is commented out,
// because this caused trouble under ewe-vm v1.49 on win-xp
// when MovingMap was started with maximized CacheWolf-Window
// this.windowFlagsToClear = WindowConstants.FLAG_HAS_TITLE | UIConstants.BDR_NOBORDER;
this.hasTopBar = false;
this.noBorder = true;
this.setPreferredSize(Preferences.itself().getScreenWidth(), Preferences.itself().getScreenHeight());
this.title = "Moving Map";
// background must not be black because black is interpreted as transparent
// and transparent images above (eg trackoverlay) want be drawn in windows-VM,
// so be care, don|t use white either
this.backGround = new Color(254, 254, 254);
mmp = new MovingMapPanel(this);
this.addLast(mmp);
if (Vm.isMobile() && Preferences.itself().getScreenWidth() >= 400)
mobileVGA = true;
String imagesize = "";
if (mobileVGA)
imagesize = "_vga";
// Symbol Position
posCircle = new MapSymbol("GPS-Position", null, new CWPoint());
posCircle.properties = mImage.AlwaysOnTop;
mmp.addImage(posCircle);
posCircleImageHaveSignal = new AniImage("position_green" + imagesize + ".png");
posCircleImageNoSignal = new AniImage("position_yellow" + imagesize + ".png");
posCircleImageNoGps = new AniImage("position_red" + imagesize + ".png");
// Symbol directionArrows
directionArrows.properties = mImage.AlwaysOnTop;
mmp.addImage(directionArrows);
final int fontSize = (3 * Preferences.itself().fontSize) / 2;
final Font imageFont = new Font("Helvetica", Font.PLAIN, fontSize);
fm = getFontMetrics(imageFont);
GpsStatus = noGPS;
dontUpdatePos = false;
ignoreGps = true;
mmp.startDragResolution = 5;
mapsloaded = false;
scaleWanted = 1;
mapChangeModus = HIGHEST_RESOLUTION_GPS_DEST;
lastHighestResolutionGPSDestScale = -1;
controlsLayer = new MovingMapControls(this);
}
/**
* Showing the moving map.
*/
public void display(CWPoint centerTo) {
exec(); // displays the Form modal
running = true;
ignoreGps = true; // else overlay symbols are removed on started gps
rebuildOverlaySet(); // show tracks , even if reentering map
scaleWanted = Preferences.itself().lastScale;
mapChangeModus = NORMAL_KEEP_RESOLUTION;
// a cache could have been deleted (previously shown)
this.removeAllMapSymbols();
initMaps(centerTo);
if (symbols.isEmpty()) {
this.showCachesOnMap();
}
if (getControlsLayer() != null) {
getControlsLayer().changeRoleState(MovingMapControls.ROLE_MENU, false);
}
// if this is not running the destChanged called by navigate did nothing,
if (MainTab.itself.navigate.destinationIsCache) {
destChanged(MainTab.itself.navigate.destinationCache);
} else {
if (Navigate.destination.isValid())
destChanged(Navigate.destination);
}
ignoreGps = false;
}
/**
* moving the map by MainTab.itself.navigate.gpsPos.
*/
public void updatePositionFromGps(int fix) {
if (!running || ignoreGps)
return;
// runMovingMap neccessary in case of multi-threaded Java-VM:
// ticked could be called during load of mmp
if ((fix > 0) && (Navigate.gpsPos.getSats() >= 0)) {
// TODO is getSats really necessary?
directionArrows.setDirections((float) Navigate.gpsPos.getBearing(Navigate.destination), (float) MainTab.itself.navigate.skyOrientationDir.lonDec, (float) Navigate.gpsPos.getBear());
setGpsStatus(MovingMap.gotFix);
updatePosition(Navigate.gpsPos);
ShowLastAddedPoint(MainTab.itself.navigate.curTrack);
}
if (fix == 0 && Navigate.gpsPos.getSats() == 0)
setGpsStatus(MovingMap.lostFix);
if (fix < 0)
setGpsStatus(MovingMap.noGPSData);
controlsLayer.updateContent("hdop", Convert.toString(Navigate.gpsPos.getHDOP()));
controlsLayer.updateContent("sats", Convert.toString(Navigate.gpsPos.getSats()) + "/" + Convert.toString(Navigate.gpsPos.getSatsInView()));
}
public void destChanged(CWPoint d) {
if (!running || gotoPos != null && gotoPos.where.equals(d))
return;
removeMapSymbol("goto");
gotoPos = addSymbol("goto", "goto_map.png", d);
repaint();
}
public void destChanged(CacheHolder ch) {
final CWPoint d = new CWPoint(ch.getWpt()); // d is never null
if (!running || !d.isValid() || gotoPos != null && gotoPos.where.equals(d))
return;
removeMapSymbol("goto");
gotoPos = addSymbol("goto", ch, "goto_map.png", d);
repaint();
}
/**
* Method to load the best map for lat/lon and move the map so that the posCircle is at lat/lon
*/
private void updatePosition(CWPoint where) {
if (inUpdatePosition || dontUpdatePos || (where.latDec == 0 && where.lonDec == 0) || !where.isValid())
return; // avoid multi-threading problems
inUpdatePosition = true;
Point mapPos = updatePositionOfMainMapImage(where);
forceMapLoad = forceMapLoad || lastWidth != this.width || lastHeight != this.height;
lastWidth = width;
lastHeight = height;
// if more then 1/10 of screen moved since last time or forceMapLoad:
if (forceMapLoad || (java.lang.Math.abs(lastCompareX - mapPos.x) > this.width / 10 || java.lang.Math.abs(lastCompareY - mapPos.y) > this.height / 10)) {
// we try to find a better map
if (autoSelectMap) {
setBestMap(where, !mmp.ScreenCompletlyCoveredByMainMap(this.width, this.height));
mapPos = getMapPositionOnScreen();
forceMapLoad = false;
}
if (isFillWhiteArea() && !mmp.ScreenCompletlyCoveredByMainMap(this.width, this.height))
fillWhiteArea();
lastCompareX = mapPos.x;
lastCompareY = mapPos.y;
} else {
mmp.updatePositionOfMapTiles(mapPos.x - lastXPos, mapPos.y - lastYPos);
}
updatePositionOfMapElements();
lastXPos = mapPos.x;
lastYPos = mapPos.y;
inUpdatePosition = false;
repaint();
}
public MovingMapControls getControlsLayer() {
return controlsLayer;
}
public void resizeTo(int w, int h) {
super.resizeTo(w, h);
updateFormSize(w, h);
}
public void updateFormSize(int w, int h) {
MapImage.setScreenSize(w, h);
MapImage mainMap = mmp.getMainMap();
directionArrows.setLocation(w / 2 - directionArrows.getWidth() / 2, 10);
if (mainMap != null)
mainMap.screenDimChanged();
if (posCircle != null)
posCircle.screenDimChanged();
if (tracks != null)
rebuildOverlaySet();
// TODO: see if the rest of the code works with symbols = null
for (int i = symbols.size() - 1; i >= 0; i--) {
((MapSymbol) symbols.get(i)).screenDimChanged();
}
if (controlsLayer != null) {
controlsLayer.updateFormSize(w, h);
}
}
/**
* loads the list of maps
*
* @param lat
* used to create empty maps with correct conversion from lon to meters the latitude must be known
*/
private void loadMaps(double lat) {
if (inLoadMaps)
return;
if (this.maps != null) {
if (!(MainForm.profile.getMapsDir().equals(this.maps.getDirOfMapsList()))) {
mapsloaded = false;
}
}
if (mapsloaded)
return;
inLoadMaps = true;
final InfoBox inf = new InfoBox(MyLocale.getMsg(4201, "Info"), MyLocale.getMsg(4203, "Loading list of maps..."));
Vm.showWait(this, true);
inf.exec();
inf.waitUntilPainted(100);
boolean remember = dontUpdatePos;
dontUpdatePos = true;
maps = new MapsList(lat);
resetCenterOfMap();
dontUpdatePos = remember;
inf.close(0);
Vm.showWait(this, false);
mapsloaded = true;
inLoadMaps = false;
}
public void updateScale() {
if (currentMap != null) {
double lineLengthMeters = 40 * currentMap.scale;
final int metricSystem = Preferences.itself().metricSystem;
double localizedLineLength = 0;
int bigUnit = -1;
int smallUnit = -1;
double threshold = -1;
// Allow for different metric systems
if (metricSystem == Metrics.IMPERIAL) {
bigUnit = Metrics.MILES;
smallUnit = Metrics.FEET;
threshold = 501;
localizedLineLength = Metrics.convertUnit(lineLengthMeters, Metrics.METER, smallUnit);
} else {
bigUnit = Metrics.KILOMETER;
smallUnit = Metrics.METER;
threshold = 1000;
localizedLineLength = lineLengthMeters;
}
int currentUnit = smallUnit;
float digits = (float) java.lang.Math.floor(java.lang.Math.log(localizedLineLength) / java.lang.Math.log(10.0));
localizedLineLength = (float) java.lang.Math.ceil(localizedLineLength / (float) java.lang.Math.pow(10, digits)) * (float) java.lang.Math.pow(10, digits);
if (localizedLineLength >= threshold) {
currentUnit = bigUnit;
localizedLineLength = Metrics.convertUnit(lineLengthMeters, Metrics.METER, currentUnit);
digits = (float) java.lang.Math.floor(java.lang.Math.log(localizedLineLength) / java.lang.Math.log(10.0));
localizedLineLength = (float) java.lang.Math.ceil(localizedLineLength / (float) java.lang.Math.pow(10, digits)) * (float) java.lang.Math.pow(10, digits);
}
String lineLengthString = Convert.toString((int) localizedLineLength) + Metrics.getUnit(currentUnit);
if (digits < 0) {
final Double tmp = new Double();
tmp.set(localizedLineLength);
final int decimals = (int) (-1 * digits);
lineLengthString = tmp.toString(decimals + 2, decimals, 0) + Metrics.getUnit(currentUnit);
// lineLengthString = MyLocale.formatDouble(tmp,"0.000") +
// Metrics.getUnit(currentUnit);
}
lineLengthMeters = Metrics.convertUnit(localizedLineLength, currentUnit, Metrics.METER);
final int lineLengthPixels = (int) java.lang.Math.round(lineLengthMeters / currentMap.scale);
controlsLayer.updateContent("scale", lineLengthString, lineLengthPixels);
} else {
controlsLayer.updateContent("scale", "no map", 20);
}
}
private void updateDistance() {
if (gotoPos != null && posCircle.where.isValid()) {
final double currentDistance = gotoPos.where.getDistance(posCircle.where);
if (currentDistance != lastDistance) {
lastDistance = currentDistance;
final ewe.sys.Double dd = new ewe.sys.Double();
String d;
final int metricSystem = Preferences.itself().metricSystem;
double localizedDistance = 0;
int bigUnit = -1;
int smallUnit = -1;
double threshold = -1;
// Allow for different metric systems
if (metricSystem == Metrics.IMPERIAL) {
// Why these values? See: http://tinyurl.com/b4nn9m
bigUnit = Metrics.MILES;
smallUnit = Metrics.FEET;
threshold = 0.1;
localizedDistance = Metrics.convertUnit(currentDistance, Metrics.KILOMETER, Metrics.MILES);
} else {
bigUnit = Metrics.KILOMETER;
smallUnit = Metrics.METER;
threshold = 1.0;
localizedDistance = currentDistance;
}
dd.set(localizedDistance);
if (dd.value >= threshold) {
d = MyLocale.formatDouble(dd, "0.000") + Metrics.getUnit(bigUnit);
} else {
dd.set(Metrics.convertUnit(dd.value, bigUnit, smallUnit));
d = dd.toString(3, 0, 0) + Metrics.getUnit(smallUnit);
}
controlsLayer.updateContent("distance", d);
}
} else {
controlsLayer.updateContent("distance", "");
}
}
public void addTrack(Track tr) {
if (tr == null)
return;
if (tracks == null)
tracks = new Vector();
if (tracks.find(tr) >= 0)
return; // track already in list
tracks.add(tr);
rebuildOverlaySet();
}
public void addTracks(Track[] trs) {
if (trs == null || trs.length == 0)
return;
for (int i = 0; i < trs.length; i++) {
addTrack(trs[i]);
}
rebuildOverlaySet();
}
/**
* adds an 3x3 set of overlays to the map-window which contain the track
*
* add tracks with addtrack(track) before
*/
public void addOverlaySet() {
if (tracks == null)
return; // no tracks
try {
TrackOverlaySetCenterTopLeft = ScreenXY2LatLon(100, 100);
addMissingOverlays();
} catch (final NullPointerException e) {
// hapens if currentMap == null or PosCircle not valid
} catch (final IllegalArgumentException e) {
// happens if screensize is still not known ---> in both cases
// creation of Overlayset will be done in updateOverlayPos if
// tracks != null
}
}
public void destroyOverlaySet() {
if (TrackOverlays != null) {
for (int i = 0; i < TrackOverlays.length; i++) {
destroyOverlay(i);
}
}
Vm.getUsedMemory(true); // call garbage collection
Vm.gc();
}
public void rebuildOverlaySet() {
destroyOverlaySet();
addOverlaySet();
}
public void addMissingOverlays() {
if (currentMap == null || (!posCircle.where.isValid()) || width == 0 || height == 0)
return; // height == 0 happens if this is called before the form
// ist displayed on the screen
if (TrackOverlays == null) {
TrackOverlays = new TrackOverlay[9];
TrackOverlaySetCenterTopLeft = ScreenXY2LatLon(100, 100);
}
// avoid multi-threading problems
final boolean remember = dontUpdatePos;
dontUpdatePos = true;
final Point upperleftOf4 = getXYonScreen(TrackOverlaySetCenterTopLeft);
int i;
for (int yi = 0; yi < 3; yi++) {
for (int xi = 0; xi < 3; xi++) {
i = yi * 3 + xi;
if (TrackOverlays[i] == null) {
Preferences.itself().log("addMissingOverlays: widht: " + width + ", height: " + height);
TrackOverlays[i] = new TrackOverlay(ScreenXY2LatLon(upperleftOf4.x + (xi - 1) * width, upperleftOf4.y + (yi - 1) * height), width, height, currentMap);
TrackOverlays[i].setLocation(width + 1, height + 1);
// outside of the screen will hide it automatically
// it will get the correct position in upadteOverlayposition
TrackOverlays[i].tracks = this.tracks;
TrackOverlays[i].paintTracks();
mmp.addImage(TrackOverlays[i]);
}
}
}
updateOverlayOnlyPos();
MapImage mainMap = mmp.getMainMap();
if (mainMap != null)
mmp.images.moveToBack(mainMap);
dontUpdatePos = remember;
}
private void destroyOverlay(int ov) {
if (TrackOverlays[ov] == null)
return;
mmp.removeImage(TrackOverlays[ov]);
TrackOverlays[ov].free();
TrackOverlays[ov] = null;
}
public void rearangeOverlays() {
final Point oldp = getXYonScreen(TrackOverlaySetCenterTopLeft);
if (TrackOverlays[1].isOnScreen()) { // oben raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x, oldp.y - 2 * height));
destroyOverlay(6);
destroyOverlay(7);
destroyOverlay(8);
mmp.removeImage(TrackOverlays[0]);
mmp.removeImage(TrackOverlays[1]);
mmp.removeImage(TrackOverlays[2]);
TrackOverlays[6] = TrackOverlays[0];
TrackOverlays[7] = TrackOverlays[1];
TrackOverlays[8] = TrackOverlays[2];
mmp.addImage(TrackOverlays[6]);
mmp.addImage(TrackOverlays[7]);
mmp.addImage(TrackOverlays[8]);
TrackOverlays[0] = null;
TrackOverlays[1] = null;
TrackOverlays[2] = null;
destroyOverlay(3);
destroyOverlay(4);
destroyOverlay(5);
} else {
if (TrackOverlays[3].isOnScreen()) { // links raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x - 2 * width, oldp.y));
destroyOverlay(2);
destroyOverlay(5);
destroyOverlay(8);
mmp.removeImage(TrackOverlays[0]);
mmp.removeImage(TrackOverlays[3]);
mmp.removeImage(TrackOverlays[6]);
TrackOverlays[2] = TrackOverlays[0];
TrackOverlays[5] = TrackOverlays[3];
TrackOverlays[8] = TrackOverlays[6];
mmp.addImage(TrackOverlays[2]);
mmp.addImage(TrackOverlays[5]);
mmp.addImage(TrackOverlays[8]);
TrackOverlays[0] = null;
TrackOverlays[3] = null;
TrackOverlays[6] = null;
destroyOverlay(1);
destroyOverlay(4);
destroyOverlay(7);
} else {
if (TrackOverlays[5].isOnScreen()) { // rechts raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x + 2 * width, oldp.y));
destroyOverlay(0);
destroyOverlay(3);
destroyOverlay(6);
mmp.removeImage(TrackOverlays[2]);
mmp.removeImage(TrackOverlays[5]);
mmp.removeImage(TrackOverlays[8]);
TrackOverlays[0] = TrackOverlays[2];
TrackOverlays[3] = TrackOverlays[5];
TrackOverlays[6] = TrackOverlays[8];
mmp.addImage(TrackOverlays[0]);
mmp.addImage(TrackOverlays[3]);
mmp.addImage(TrackOverlays[6]);
TrackOverlays[2] = null;
TrackOverlays[5] = null;
TrackOverlays[8] = null;
destroyOverlay(1);
destroyOverlay(4);
destroyOverlay(7);
} else {
if (TrackOverlays[7].isOnScreen()) { // unten raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x, oldp.y + 2 * height));
destroyOverlay(0);
destroyOverlay(1);
destroyOverlay(2);
mmp.removeImage(TrackOverlays[6]);
mmp.removeImage(TrackOverlays[7]);
mmp.removeImage(TrackOverlays[8]);
TrackOverlays[0] = TrackOverlays[6];
TrackOverlays[1] = TrackOverlays[7];
TrackOverlays[2] = TrackOverlays[8];
mmp.addImage(TrackOverlays[0]);
mmp.addImage(TrackOverlays[1]);
mmp.addImage(TrackOverlays[2]);
TrackOverlays[6] = null;
TrackOverlays[7] = null;
TrackOverlays[8] = null;
destroyOverlay(3);
destroyOverlay(4);
destroyOverlay(5);
} else { // it is important to test for diagonal only
// if the other didn't match
if (TrackOverlays[0].isOnScreen()) { // links
// oben raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x - 2 * width, oldp.y - 2 * height));
destroyOverlay(8);
mmp.removeImage(TrackOverlays[0]);
TrackOverlays[8] = TrackOverlays[0];
mmp.addImage(TrackOverlays[8]);
TrackOverlays[0] = null;
destroyOverlay(1);
destroyOverlay(2);
destroyOverlay(3);
destroyOverlay(4);
destroyOverlay(5);
destroyOverlay(6);
destroyOverlay(7);
} else {
if (TrackOverlays[2].isOnScreen()) { // rechts
// oben
// raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x + 2 * width, oldp.y - 2 * height));
destroyOverlay(6);
mmp.removeImage(TrackOverlays[2]);
TrackOverlays[6] = TrackOverlays[2];
mmp.addImage(TrackOverlays[6]);
TrackOverlays[2] = null;
destroyOverlay(0);
destroyOverlay(1);
destroyOverlay(3);
destroyOverlay(4);
destroyOverlay(5);
destroyOverlay(7);
destroyOverlay(8);
} else {
if (TrackOverlays[6].isOnScreen()) { // links
// unten
// raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x - 2 * width, oldp.y + 2 * height));
destroyOverlay(2);
mmp.removeImage(TrackOverlays[6]);
TrackOverlays[2] = TrackOverlays[6];
mmp.addImage(TrackOverlays[2]);
TrackOverlays[6] = null;
destroyOverlay(0);
destroyOverlay(1);
destroyOverlay(3);
destroyOverlay(4);
destroyOverlay(5);
destroyOverlay(7);
destroyOverlay(8);
} else {
if (TrackOverlays[8].isOnScreen()) { // rechts
// unten
// raus
TrackOverlaySetCenterTopLeft.set(ScreenXY2LatLon(oldp.x + 2 * width, oldp.y + 2 * height));
destroyOverlay(0);
mmp.removeImage(TrackOverlays[8]);
TrackOverlays[0] = TrackOverlays[8];
mmp.addImage(TrackOverlays[0]);
TrackOverlays[8] = null;
destroyOverlay(1);
destroyOverlay(2);
destroyOverlay(3);
destroyOverlay(4);
destroyOverlay(5);
destroyOverlay(6);
destroyOverlay(7);
} else
for (int i = 0; i < TrackOverlays.length; i++) {
destroyOverlay(i);
TrackOverlaySetCenterTopLeft = ScreenXY2LatLon(100, 100);
} // this happens if a position
// jump occured
}
}
}
}
}
}
} // close all IFs
Vm.gc(); // call garbage collection
}
public void ShowLastAddedPoint(Track tr) {
if (TrackOverlays == null || tr == null)
return;
for (int i = 0; i < TrackOverlays.length; i++) {
TrackOverlays[i].paintLastAddedPoint(tr);
}
}
public void updateOverlayOnlyPos() {
if (TrackOverlays == null || TrackOverlays[4] == null)
return;
// Point upperleft = getMapXYPosition();
Point posOnScreen;
posOnScreen = getXYonScreen(TrackOverlays[4].topLeft);
final Dimension ws = mmp.getSize(null);
final int ww = ws.width;
final int wh = ws.height;
// Vm.sleep(100); // this is necessary because the ewe vm ist not
// multi-threaded and the serial thread also needs time
int num, pX, pY;
for (int yi = 0; yi < 3; yi++) {
for (int xi = 0; xi < 3; xi++) {
num = yi * 3 + xi;
pX = posOnScreen.x + (xi - 1) * ww;
pY = posOnScreen.y + (yi - 1) * wh;
TrackOverlays[num].setLocation(pX, pY);
}
}
}
public void updateOverlayPos() {
if (tracks == null || tracks.size() == 0)
return;
if (TrackOverlays == null || TrackOverlays[4] == null)
addMissingOverlays();
else {
updateOverlayOnlyPos();
if (TrackOverlays[0].locAlways.x > 0 || TrackOverlays[2].locAlways.x < 0 || TrackOverlays[0].locAlways.y > 0 || TrackOverlays[8].locAlways.y < 0) { // testForNeedToRearange
rearangeOverlays();
addMissingOverlays();
// updateOverlayOnlyPos(); is called from addMissingOverlays
}
}
}
/**
* move posCircle to the Centre of the Screen
*
*/
private void resetCenterOfMap() {
if (width != 0) {
posCircleX = width / 2;
posCircleY = height / 2;
} else {
// maybe this could / should be replaced to windows size
Preferences.itself().log("Window not yet on screen? This should never happen (again)");
posCircleX = Preferences.itself().getScreenWidth() / 2;
posCircleY = Preferences.itself().getScreenHeight() / 2;
}
posCircle.hidden = false;
// always position the middle of a symbol
posCircle.move(posCircleX - posCircle.getWidth() / 2, posCircleY - posCircle.getHeight() / 2);
// posCircle.setLocation a problem -> hiding the posCircle in some situation
}
public void movePosCircleToLatLon(CWPoint p, boolean neuZeichnen) {
moveScreenXYtoLatLon(new Point(posCircleX, posCircleY), p, neuZeichnen);
}
public void moveScreenXYtoLatLon(Point s, CWPoint c, boolean neuZeichnen) {
final Point mappos = getMapPositionOnScreen();
final Point onscreenpos = getXYonScreen(c);
if (mmp != null) {
MapImage mainMap = mmp.getMainMap();
if (mainMap != null)
mainMap.move(mappos.x - onscreenpos.x + s.x, mappos.y - onscreenpos.y + s.y);
}
mapMoved(s.x - onscreenpos.x, s.y - onscreenpos.y);
if (neuZeichnen)
mmp.repaintNow();
}
/**
* call this if the map has moved on the screen (Ex by dragging) or size of posCircle changed this routine will adjust (move accordingly) all other symbols on the screen
*
* @param diffX
* @param diffY
*/
public void mapMoved(int diffX, int diffY) {
final int npx = posCircleX - posCircle.getWidth() / 2 + diffX;
final int npy = posCircleY - posCircle.getHeight() / 2 + diffY;
posCircle.move(npx, npy);
posCircleX = posCircleX + diffX;
posCircleY = posCircleY + diffY;
dontUpdatePos = false;
updatePosition(posCircle.where);
}
/**
* the map-position is calculated relativ to posCircle<br>
* this is called when the map needs to be moved<br>
* <br>
* is used to move the map to the correct point<br>
* <br>
*
* @return Point position of map on window (upper left corner)<br>
* returns the same as mmp.mapImage.getLocation(mapPos);<br>
* but also works if mmp == null<br>
*/
private Point getMapPositionOnScreen() {
// in case no calculation is possible return something outside of the screen
if (currentMap == null || !posCircle.where.isValid())
return new Point(Preferences.itself().getScreenWidth() + 1, Preferences.itself().getScreenHeight() + 1);
final Point mapPos = new Point();
final Point mapposint = currentMap.calcMapXY(posCircle.where);
mapPos.x = posCircleX - mapposint.x;
mapPos.y = posCircleY - mapposint.y;
return mapPos;
}
/**
* Method to calculate Point x,y of the current map using lat and lon target coordinates. There ist no garanty that the returned coordinates are inside of the map. They can be negative.
*
* @param ll
* CoordinatePoint
* @return Point
*/
public Point getXYonScreen(CoordinatePoint ll) {
if (currentMap == null)
return null;
final Point coords = currentMap.calcMapXY(ll);
final Point mapPos = getMapPositionOnScreen();
return new Point(coords.x + mapPos.x, coords.y + mapPos.y);
}
public CWPoint ScreenXY2LatLon(int px, int py) {
final Point mapPos = getMapPositionOnScreen();
return currentMap.calcLatLon(px - mapPos.x, py - mapPos.y);
}
public MapSymbol addSymbol(String pName, String filename, CWPoint where) {
final MapSymbol ms = new MapSymbol(pName, filename, where);
setSymbolLocation(ms, where);
addMapSymbol(ms);
return ms;
}
public MapSymbol addSymbol(String pName, Object mapObject, String filename, CWPoint where) {
final MapSymbol ms = new MapSymbol(pName, mapObject, filename, where);
setSymbolLocation(ms, where);
addMapSymbol(ms);
return ms;
}
public void addSymbol(String pName, Object mapObject, Image imSymb, CWPoint where) {
final MapSymbol ms = new MapSymbol(pName, mapObject, imSymb, where);
setSymbolLocation(ms, where);
addMapSymbol(ms);
}
private void setSymbolLocation(MapSymbol symbol, CoordinatePoint where) {
final Point pOnScreen = getXYonScreen(where);
if (pOnScreen != null) {
symbol.setLocation(pOnScreen.x - symbol.getWidth() / 2, pOnScreen.y - symbol.getHeight() / 2);
}
}
private void addMapSymbol(MapSymbol mapSymbol) {
mapSymbol.properties |= mImage.AlwaysOnTop;
symbols.add(mapSymbol);
mmp.addImage(mapSymbol); // add to mmp list
}
public void updateSymbolPositions() {
showCachesOnMap();
for (int i = symbols.size() - 1; i >= 0; i--) {
updateSymbolPosition((MapSymbol) symbols.get(i));
}
}
private void updateSymbolPosition(MapSymbol symbol) {
Point pOnScreen = getXYonScreen(symbol.where);
if (pOnScreen != null) {
symbol.move(pOnScreen.x - symbol.getWidth() / 2, pOnScreen.y - symbol.getHeight() / 2);
}
}
private void addSymbolIfNecessary(String pName, Object mapObject, Image imSymb, CWPoint where) {
if (findMapSymbol(pName) >= 0)
return;
else
addSymbol(pName, mapObject, imSymb, where);
}
public void addSymbolOnTop(String pName, Object mapObject, String filename, CWPoint where) {
removeMapSymbol(mapObject);
addSymbol(pName, mapObject, filename, where);
}
public CWPoint getGotoPosWhere() {
if (gotoPos == null)
return null;
else
return gotoPos.where;
}
public void removeAllMapSymbols() {
for (int i = symbols.size() - 1; i >= 0; i--) {
mmp.removeImage((MapSymbol) symbols.get(i));
}
symbols.removeAllElements();
}
public void removeMapSymbol(String pName) {
final int symbNr = findMapSymbol(pName);
if (symbNr != -1)
removeMapSymbol(symbNr);
}
public void removeMapSymbol(Object obj) {
final int symbNr = findMapSymbol(obj);
if (symbNr != -1)
removeMapSymbol(symbNr);
}
public void removeMapSymbol(int SymNr) {
mmp.removeImage(((MapSymbol) symbols.get(SymNr)));
symbols.removeElementAt(SymNr);
}
public int findMapSymbol(String pName) {
MapSymbol ms;
for (int i = symbols.size() - 1; i >= 0; i--) {
ms = (MapSymbol) symbols.get(i);
if (ms.name == pName)
return i;
}
return -1;
}
public int findMapSymbol(Object obj) {
MapSymbol ms;
for (int i = symbols.size() - 1; i >= 0; i--) {
ms = (MapSymbol) symbols.get(i);
if (ms.mapObject == obj)
return i;
}
return -1;
}
private void updatePositionOfMapElements() {
updateSymbolPositions();
updateDistance();
updateOverlayPos();
}
public Point updatePositionOfMainMapImage(CWPoint where) {
posCircle.where.set(where);
final Point mapPos = getMapPositionOnScreen();
mmp.moveMainMapImage(mapPos.x, mapPos.y);
return mapPos;
}
private void showCachesOnMap() {
CacheHolder ch;
final BoundingBox screenArea = new BoundingBox(ScreenXY2LatLon(0, 0), ScreenXY2LatLon(width, height));
for (int i = cacheDB.size() - 1; i >= 0; i--) {
ch = cacheDB.get(i);
if (screenArea.isInBound(ch.getWpt())) {
// because visible and valid don't change while showing map
// -->need no remove
if (ch.isVisible() && ch.getWpt().isValid()) {
if (Preferences.itself().showCachesOnMap) {
addSymbolIfNecessary(ch.getCode(), ch, CacheType.getBigCacheIcon(ch), ch.getWpt());
} else {
if (ch.isChecked || ch == cacheDB.get(MainTab.itself.tablePanel.getSelectedCache())) {
addSymbolIfNecessary(ch.getCode(), ch, CacheType.getBigCacheIcon(ch), ch.getWpt());
} else {
removeMapSymbol(ch);
}
}
}
} else {
removeMapSymbol(ch);
}
}
// adding target and selected
// show target
if (gotoPos != null) {
// the CacheHolder Symbol must be inserted too, even if not marked (if it is Cache)
CacheHolder gotoPosCH = null;
if (gotoPos.mapObject instanceof CacheHolder) {
gotoPosCH = (CacheHolder) gotoPos.mapObject;
}
if (gotoPosCH != null) {
if (screenArea.isInBound(gotoPosCH.getWpt())) {
if (!Preferences.itself().showCachesOnMap) {
addSymbolIfNecessary(gotoPosCH.getCode(), gotoPosCH, CacheType.getBigCacheIcon(gotoPosCH), gotoPosCH.getWpt());
}
addSymbolIfNecessary("goto", gotoPosCH, imgGoto, gotoPos.where);
}
}
}
// mark Selected
removeMapSymbol("selectedCache");
ch = cacheDB.get(MainTab.itself.tablePanel.getSelectedCache());
if (ch != null) {
if (screenArea.isInBound(ch.getWpt())) {
addSymbolIfNecessary("selectedCache", ch, imgSelectedCache, ch.getWpt());
}
}
}
boolean reflectResourceException = true;
private void fillWhiteArea() {
Preferences.itself().log("filling white area");
MapImage mainMap = mmp.getMainMap();
if (mainMap == null)
return; // if error at map load
try {
Vm.showWait(true);
eventOccurred = false;
whiteAreas.clear();
// calculate areas which will not drawn
final Point mapPos = getMapPositionOnScreen();
if (mapPos.x > this.width || mapPos.y > this.height || mapPos.x + mainMap.getWidth() < 0 || mapPos.y + mainMap.getHeight() < 0) {
Preferences.itself().log("map is outside the screen --> you only need to fill the screen");
whiteAreas.add(new Rect(0, 0, this.width, this.height));
} else {
final Rect whiteArea = new Rect((-this.width / 10), (-this.height / 10), (int) (this.width * 1.1), (int) (this.height * 1.1));
final Rect blackArea = new Rect(mapPos.x, mapPos.y, mainMap.getWidth(), mainMap.getHeight());
Preferences.itself().log("max wA to cover: " + whiteArea);
addRemainingWhiteAreas(blackArea, whiteArea);
}
// I've sometimes experienced an endless loop which might be caused by a bug in getBestMap.
// Therefore i will stop the loop after max runs
int max = 100;
int count = 0;
mmp.clearMapTiles();
MovingMapCache.movingMapCache().clearUsedFlags();
while (isFillWhiteArea() && currentMap.zoomFactor == 1.0 && !mapHidden && !whiteAreas.isEmpty() && count < max && !eventOccurred) {
count++;
Preferences.itself().log(eventOccurred + " white Area Nr.: " + count);
try {
Rect stillWhite = getMapTileForWhiteArea();
if (stillWhite != null) {
int newWidth = stillWhite.width;
int newHeight = stillWhite.height;
if (stillWhite.width > smallTileWidth) {
newWidth = stillWhite.width / 2;
}
if (stillWhite.height > smallTileHeight) {
newHeight = stillWhite.height / 2;
}
if (!(newWidth == stillWhite.width && newHeight == stillWhite.height)) {
addRemainingWhiteAreas(new Rect(stillWhite.x + newWidth, stillWhite.y + newHeight, 0, 0), stillWhite);
}
}
} catch (final ewe.sys.SystemResourceException sre) {
// next time there may be problem don't ask again
if (reflectResourceException) {
if (new InfoBox(MyLocale.getMsg(5500, "Error"), "Not enough ressources to fill white ares, disabling this").wait(FormBase.YESB | FormBase.NOB) == FormBase.IDYES) {
setFillWhiteArea(false);
reflectResourceException = true;
} else {
reflectResourceException = false;
}
}
}
}
} finally {
// Remove all tiles not needed from the cache to reduce memory
MovingMapCache.movingMapCache().cleanCache();
Vm.showWait(false);
}
}
private Rect getMapTileForWhiteArea() {
Rect blackArea;
final Rect whiteArea = (Rect) whiteAreas.get(0);
whiteAreas.removeElementAt(0);
// calculate the center of the rectangle and try to get an map for it
final int middleX = whiteArea.x + (whiteArea.width) / 2;
final int middleY = whiteArea.y + (whiteArea.height) / 2;
final CWPoint centerPoint = ScreenXY2LatLon(middleX, middleY);
final Rect screen = new Rect();
screen.height = whiteArea.height;
screen.width = whiteArea.width;
final MapInfoObject bestMap = maps.getBest(centerPoint, screen, currentMap.scale, true, false);
if (bestMap == null) {
// No map found, area must be left white
Preferences.itself().log("!For wA " + whiteArea + middleX + "," + middleY + " got no map");
return whiteArea;
}
// perhaps a nearby map is found, not containing the (center)Point, perhaps it fits on the screen
// but we can't use this map: the splitting into white areas goes wrong in that case
if (!(bestMap.bottomright.latDec <= centerPoint.latDec && centerPoint.latDec <= bestMap.topleft.latDec)) {
Preferences.itself().log("!For wA " + whiteArea + middleX + "," + middleY + " Lat outside " + bestMap.getMapNameForList());
return whiteArea;
}
if (!(bestMap.topleft.lonDec <= centerPoint.lonDec && centerPoint.lonDec <= bestMap.bottomright.lonDec)) {
Preferences.itself().log("!For wA " + whiteArea + middleX + "," + middleY + " Lon outside " + bestMap.getMapNameForList());
return whiteArea;
}
final String imagefilename = bestMap.getImagePathAndName();
if (!imagefilename.equals(currentMap.getImagePathAndName())) {
if (imagefilename.length() > 0) {
// calculate position of the new map on the screen
final Point mapPos = new Point();
final Point mapposint = bestMap.calcMapXY(posCircle.where);
mapPos.x = posCircleX - mapposint.x;
mapPos.y = posCircleY - mapposint.y;
final Point mapDimension = bestMap.calcMapXY(bestMap.bottomright);
blackArea = new Rect(mapPos.x, mapPos.y, mapDimension.x, mapDimension.y);
// Not all maps have the dimension 1000x1000 Pixels, we cache this information:
Dimension rect2 = MovingMapCache.movingMapCache().getDimension(imagefilename);
MapImage fullImage = null;
if (rect2 == null) {
// the map is not in the cache
fullImage = new MapImage(imagefilename);
if (fullImage.image != null) {
rect2 = new Dimension(fullImage.getHeight(), fullImage.getWidth());
MovingMapCache.movingMapCache().putDimension(imagefilename, rect2);
} else {
Preferences.itself().log("Error getting bestMap from file: " + imagefilename);
maps.remove(bestMap);
return whiteArea;
}
}
// really adding a map image / tiles of the map image to the MovingMapPanel
if (!generateSmallTiles(blackArea, imagefilename, mapPos, rect2, fullImage)) {
Preferences.itself().log("Error generate SmallTiles from file: " + imagefilename);
maps.remove(bestMap);
return whiteArea;
}
Preferences.itself().log("For wA " + whiteArea + middleX + "," + middleY + " got " + blackArea + " ='" + bestMap.getMapNameForList() + "'");
// Are there any white areas left?
addRemainingWhiteAreas(blackArea, whiteArea);
}
}
return null;
}
private boolean generateSmallTiles(Rect blackArea, String filename, Point mapPos, Dimension rect2, MapImage fullImage) {
// Generate tiles from the map
final int numRows = ((rect2.height - 1) / smallTileHeight) + 1;
final int numCols = ((rect2.width - 1) / smallTileWidth) + 1;
for (int row = 0; row < numRows; row++) {
for (int column = 0; column < numCols; column++) {
// Tile is not needed, don't process
if (!isCoveredByBlackArea(mapPos, row, column, blackArea, rect2)) {
Preferences.itself().log("not needed");
continue;
}
// Get tile from cache or
// if not found, put tile into the cache
MapImage im = MovingMapCache.movingMapCache().get(filename, row, column);
if (im == null) {
if (fullImage == null) {
fullImage = new MapImage(filename);
}
if (fullImage.image != null) {
putImageIntoCache(filename, fullImage, mapPos, blackArea);
im = MovingMapCache.movingMapCache().get(filename, row, column);
} else
return false;
}
// If a tile has been found, draw it on the screen
if (im != null) {
im.setLocation(mapPos.x + (column * smallTileWidth), mapPos.y + (row * smallTileHeight));
mmp.addMapTile(im);
repaintNow();
}
}
}
return true;
}
private void putImageIntoCache(String filename, MapImage fullImage, Point mapPos, Rect blackArea) {
MapImage mapImage = null;
final int numRows = (fullImage.getHeight() - 1) / smallTileHeight + 1;
final int numCols = (fullImage.getWidth() - 1) / smallTileWidth + 1;
for (int row2 = 0; row2 < numRows; row2++) {
for (int column2 = 0; column2 < numCols; column2++) {
final int realWidth = java.lang.Math.min(smallTileWidth, (fullImage.getWidth() - smallTileWidth * column2));
final int realHeight = java.lang.Math.min(smallTileHeight, (fullImage.getHeight() - smallTileHeight * row2));
if (!isCoveredByBlackArea(mapPos, row2, column2, blackArea, new Dimension(fullImage.getWidth(), fullImage.getHeight()))) {
continue;
}
try {
final Image image2 = new Image(realWidth, realHeight);
final int[] pixels = new int[realWidth * realHeight];
fullImage.getPixels(pixels, 0, smallTileWidth * column2, smallTileHeight * row2, realWidth, realHeight, 0);
image2.setPixels(pixels, 0, 0, 0, realWidth, realHeight, 0);
mapImage = new MapImage();
mapImage.setImage(image2);
MovingMapCache.movingMapCache().put(filename, row2, column2, mapImage);
} catch (Exception e) {
Preferences.itself().log(e + " Error generating Tile Image from " + filename + " for " + row2 + "/" + column2 + " (" + realWidth + "x" + realHeight + ")");
}
}
}
}
private boolean isCoveredByBlackArea(Point mapPos, int row, int column, Rect blackArea, Dimension mapDimension) {
final int realWidth = java.lang.Math.min(smallTileWidth, (mapDimension.width - smallTileWidth * column));
final int realHeight = java.lang.Math.min(smallTileHeight, (mapDimension.height - smallTileHeight * row));
final int left = mapPos.x + column * smallTileWidth;
final int right = left + realWidth;
final int top = mapPos.y + row * smallTileHeight;
final int bottom = top + realHeight;
if (right < blackArea.x || bottom < blackArea.y) {
return false;
}
if (left > blackArea.x + blackArea.width || top > blackArea.y + blackArea.height) {
return false;
}
return true;
}
private void addRemainingWhiteAreas(Rect blackArea, Rect whiteArea) {
// divide into non overlapping
// remaining WhiteAreas must have width > mw and height > mh else they stay white
final int mw = 10;
final int mh = 10;
int leftWidth = Math.max(0, blackArea.x - whiteArea.x);
int rightWidth = Math.max(0, whiteArea.width - leftWidth - blackArea.width);
int bottomHeight = Math.max(0, blackArea.y - whiteArea.y);
int topYPos = blackArea.y + blackArea.height;
int topHeight = Math.max(0, whiteArea.y + whiteArea.height - topYPos);
// left Rect
if (leftWidth > mw) {
final Rect l = new Rect(whiteArea.x, whiteArea.y, leftWidth, bottomHeight + blackArea.height);
if (l.height > mh) {
whiteAreas.add(l);
Preferences.itself().log("Left : " + l);
}
}
// top Rect
if (topYPos != whiteArea.y) {
if (topHeight > mh) {
final Rect t = new Rect(whiteArea.x, topYPos, leftWidth + blackArea.width, topHeight);
if (t.width > mw) {
whiteAreas.add(t);
Preferences.itself().log("Top : " + t);
}
}
}
// right Rect
if (rightWidth > mw) {
final Rect r = new Rect(blackArea.x + blackArea.width, blackArea.y, rightWidth, blackArea.height + topHeight);
if (r.height > mh) {
whiteAreas.add(r);
Preferences.itself().log("Right : " + r);
}
}
// bottom Rect
if (bottomHeight > mh) {
final Rect b = new Rect(blackArea.x, whiteArea.y, blackArea.width + rightWidth, bottomHeight);
if (b.width > mw) {
whiteAreas.add(b);
Preferences.itself().log("Bottom: " + b);
}
}
}
public void gpsStarted() {
addTrack(MainTab.itself.navigate.curTrack);
ignoreGps = false;
}
public void gpsStoped() {
setGpsStatus(MovingMap.noGPS);
}
private int mapChangeModus = HIGHEST_RESOLUTION_GPS_DEST;
private float scaleWanted;
// keeps the choosen resolution as long as a map is available
// that overlaps with the screen and with the PosCircle -
// it changes the resolution if no such map is available.
// It will change back to the wanted scale as soon as a map becomes available (through movement of the GPS-receiver)
private final static int NORMAL_KEEP_RESOLUTION = 1;
private final static int HIGHEST_RESOLUTION = 2;
private final static int HIGHEST_RESOLUTION_GPS_DEST = 3;
private boolean inSetBestMap = false; // to avoid multi-threading problems
/**
* loads the best map for lat/lon according to mapChangeModus lat/lon will be at the screen-pos of posCircle when posCircle is not on the screen (shifted outside my the user) then this routine uses the centre of the screen to find the best map
* but anyway the map will be adjusted (moved) relativ to posCircle when a better map was found the called method updateposition will set posCirle Lat/-Lon to lat/lon.
*
* @param lat
* @param lon
* @param loadIfSameScale
* false: will not change the map if the better map has the same scale as the current . - this is used not to change the map, if it covers already the screen completely true: willchange the map, regardless of change in scale
*/
public void setBestMap(CWPoint where, boolean loadIfSameScale) {
if (inSetBestMap)
return;
inSetBestMap = true;
final Object[] s = getRectForMapChange(where);
final CWPoint cll = (CWPoint) s[0];
final Rect screen = (Rect) s[1];
final boolean posCircleOnScreen = ((Boolean) s[2]).booleanValue();
MapInfoObject newmap = null;
switch (mapChangeModus) {
case NORMAL_KEEP_RESOLUTION:
lastHighestResolutionGPSDestScale = -1;
newmap = maps.getBest(cll, screen, scaleWanted, false, true);
if (newmap == null)
newmap = currentMap;
break;
case HIGHEST_RESOLUTION:
lastHighestResolutionGPSDestScale = -1;
newmap = maps.getBest(cll, screen, 0.000001f, false, true);
break;
case HIGHEST_RESOLUTION_GPS_DEST:
if (gotoPos != null && GpsStatus != noGPS && posCircle.where.isValid()) {
if ((!posCircleOnScreen) && (lastHighestResolutionGPSDestScale > 0)) {
newmap = maps.getBest(cll, screen, lastHighestResolutionGPSDestScale, false, true);
} else {
// TODO use home-coos if no gps? - consider start from details panel and from gotopanel
newmap = maps.getMapForArea(posCircle.where, gotoPos.where);
// use map with most available overview if no map containing PosCircle and GotoPos is available
if (newmap == null)
newmap = maps.getBest(cll, screen, 10000000000000000000000000000000000f, false, true);
if (newmap != null) {
lastHighestResolutionGPSDestScale = newmap.scale;
if (!posCircleOnScreen) {
newmap = maps.getBest(cll, screen, lastHighestResolutionGPSDestScale, false, true);
}
}
}
}
// either Goto-Pos or GPS-Pos not set
else {
lastHighestResolutionGPSDestScale = -1;
newmap = maps.getBest(cll, screen, 0.000001f, false, true);
}
break;
default:
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4208, "Bug: \nillegal mapChangeModus: ") + mapChangeModus).wait(FormBase.OKB);
break;
}
if (newmap != null && (currentMap == null || !currentMap.getImagePathAndName().equals(newmap.getImagePathAndName()))) {
if (loadIfSameScale || !MapsList.scaleEquals(currentMap.scale / currentMap.zoomFactor, newmap)) {
// better map found
setMap(newmap, where);
moveScreenXYtoLatLon(new Point(screen.x, screen.y), cll, true);
}
inSetBestMap = false;
return;
}
if (currentMap == null && newmap == null) {
// F�r die aktuelle Position steht keine Karte zur Verf�gung
// choosemap calls setmap with posCircle-coords
posCircle.where.set(cll);
// beware: "-4" only works if the empty maps were added last (see MapsList.addEmptyMaps)
setMap(((MapListEntry) maps.elementAt(maps.getCount() - 4)).getMap(), where);
while (currentMap == null) {
// this actually cannot happen, but maybe in case of an inconstistent code change (esp. regarding empty maps)
// force the user to select a map
manualSelectMap();
if (currentMap == null)
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4210, "Moving map cannot run without a map - please select one. \n You can select an empty map")).wait(FormBase.OKB);
}
}
inSetBestMap = false;
}
/**
* method to get a point on the screen which must be included in the map the map methods are looking for.<br>
* If the poscircle is on the screen this will be that point.<br>
* If it is outside then the centre of the screen will be used.<br>
* <br>
* returns [0] = CWPoint of that point, [1] Rect describing the screen around it<br>
*
* @param ll
* lat / lon
* @return
*/
public Object[] getRectForMapChange(CWPoint ll) {
final int w = (width != 0 ? width : Preferences.itself().getScreenWidth());
// width == 0 happens if this routine is run before the windows is on the screen
final int h = (height != 0 ? height : Preferences.itself().getScreenHeight());
int pX, pY;
CWPoint cll;
Boolean posCircleOnScreen = java.lang.Boolean.FALSE;
if (posCircleX >= 0 && posCircleX <= w && posCircleY >= 0 && posCircleY <= h && ll.isValid()) {
posCircleOnScreen = java.lang.Boolean.TRUE;
// posCircle is inside the screen
pX = posCircleX;
pY = posCircleY;
// TODO
// eigentlich interessiert, ob nach dem evtl. Kartenwechsel PosCircle on Screen ist.
// So wie es jetzt ist, kann 2mal der gleiche Aufruf zum laden unterschiedlicher Karten f�hren,
// wenn vorher PosCircle nicht auf dem Schirm war, nach dem ersten Laden aber schon.
cll = new CWPoint(ll);
} else {
// when posCircle out of screen - use centre of screen as point which has to be included in the map
cll = ScreenXY2LatLon(w / 2, h / 2);
pX = w / 2;
pY = h / 2;
}
final Object[] ret = new Object[3];
ret[0] = cll;
ret[1] = new Rect(pX, pY, w, h);
ret[2] = posCircleOnScreen;
return ret;
}
private void setGpsStatus(int status) {
if (status == GpsStatus)
return;
GpsStatus = status;
dontUpdatePos = false;
ignoreGps = false;
switch (status) {
case noGPS: {
posCircle.change(null);
ignoreGps = true;
break;
}
case gotFix: {
posCircle.change(posCircleImageHaveSignal);
break;
}
case lostFix: {
posCircle.change(posCircleImageNoSignal);
break;
}
case noGPSData: {
posCircle.change(posCircleImageNoGps);
break;
}
}
// positions the posCircle correctly according to its size
// which can change when the image changes, e.g. from null to something else
mapMoved(0, 0);
posCircle.refreshNow();
}
/**
* sets and displays the map
*
* @param newmap
* @param lat
* move map so that lat/lon is in the centre / -361: don't adust to lat/lon
* @param lon
* -361: don't adust to lat/lon
*/
public void setMap(MapInfoObject newmap, CWPoint where) {
if (newmap == null) {
Preferences.itself().log("da ist was schief gelaufen: keine newmap gefunden.");
return;
}
if (currentMap != null) {
// die selbe Karte gefunden
if (newmap.getImagePathAndName().equals(currentMap.getImagePathAndName())) {
posCircle.where.set(where);
updatePositionOfMapElements();
repaint();
return;
}
}
boolean remember = dontUpdatePos;
try {
Vm.showWait(true);
dontUpdatePos = true; // make updatePosition ignore calls during loading new map
Preferences.itself().log(MyLocale.getMsg(4216, "Loading map...") + newmap.getMapNameForList());
MapImage mainMap = mmp.getMainMap();
try {
currentMap = newmap;
title = currentMap.getMapNameForList();
// neccessary to make updateposition to test if the current map is the best one for the GPS-Position
lastCompareX = Integer.MAX_VALUE;
lastCompareY = Integer.MAX_VALUE;
if (mainMap != null) {
mmp.removeImage(mainMap);
mainMap.free();
mmp.setMainMap(null);
mapImage1to1 = null;
mainMap = null;
// calls the garbage collection
// give memory free before loading the new map to avoid out of memory error
Vm.getUsedMemory(true);
}
final String ImageFilename = currentMap.getImagePathAndName();
if (ImageFilename == null) {
// no image associated with the calibration info ("empty map")
mainMap = new MapImage();
} else {
if (ImageFilename.length() > 0) {
// attention: when running in native java-vm,
// no exception will be thrown, not even OutOfMemory Error
mainMap = new MapImage(ImageFilename);
} else {
// no image file exists
mainMap = new MapImage();
}
}
mmp.setMainMap(mainMap);
mapImage1to1 = mainMap;
mainMap.properties = mainMap.properties | mImage.IsMoveable;
if (mapHidden)
mainMap.hide();
mainMap.move(0, 0);
mmp.addImage(mainMap);
mmp.images.moveToBack(mainMap);
rebuildOverlaySet();
updateAfterMapChange(where);
directionArrows.setMap(currentMap);
updateScale();
forceMapLoad = false;
} catch (final IllegalArgumentException e) {
// thrown by new AniImage() in ewe-vm if file not found;
Preferences.itself().log("[MovingMap:setMap]IllegalArgumentException", e, true);
if (mainMap != null) {
mmp.removeImage(mainMap);
mainMap.free();
mmp.setMainMap(null);
mapImage1to1 = mainMap;
}
rebuildOverlaySet();
updatePositionOfMapElements();
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4218, "Could not load map: \n") + newmap.getImagePathAndName()).wait(FormBase.OKB);
} catch (final OutOfMemoryError e) {
Preferences.itself().log("[MovingMap:setMap]OutOfMemoryError", e, true);
if (mainMap != null) {
mmp.removeImage(mainMap);
mainMap.free();
mmp.setMainMap(null);
mapImage1to1 = mainMap;
}
rebuildOverlaySet();
updatePositionOfMapElements();
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4219, "Not enough memory to load map: \n") + newmap.getImagePathAndName() + MyLocale.getMsg(4220, "\nYou can try to close\n all prgrams and \nrestart CacheWolf"))
.wait(FormBase.OKB);
} catch (final SystemResourceException e) {
Preferences.itself().log("[MovingMap:setMap]SystemResourceException", e, true);
if (mainMap != null) {
mmp.removeImage(mainMap);
mainMap.free();
mmp.setMainMap(null);
mapImage1to1 = mainMap;
}
rebuildOverlaySet();
updatePositionOfMapElements();
// TODO this doesn't work correctly if the resolution changed,
// I guess because the pixels of PosCircle will be interpreted from the new resolution,
// but should be interpreted using the old resolution to test:
// select a map with a much greater value of m per pixel manually
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4221, "Not enough ressources to load map: ") + newmap.getImagePathAndName() + MyLocale.getMsg(4220, "\nYou can try to close\n all prgrams and \nrestart CacheWolf"))
.wait(FormBase.OKB);
}
} finally {
// this doesn't work in a ticked-thread in the ewe-vm.
// That's why i made a new mThread in gotoPanel for ticked
dontUpdatePos = remember;
Vm.showWait(false);
}
}
private void updateAfterMapChange(CWPoint newCenter) {
// move mainMap
final Point centerOnMap = currentMap.calcMapXY(newCenter);
final int mapPosX = width / 2 - centerOnMap.x;
final int mapPosY = height / 2 - centerOnMap.y;
if (mmp != null) {
MapImage mainMap = mmp.getMainMap();
if (mainMap != null)
mainMap.move(mapPosX, mapPosY);
}
if (!posCircle.where.isValid()) {
posCircle.where.set(newCenter);
}
// move posCircle
final Point circlePosOnMap = currentMap.calcMapXY(posCircle.where);
posCircleX = mapPosX + circlePosOnMap.x;
posCircleY = mapPosY + circlePosOnMap.y;
final int npx = posCircleX - posCircle.getWidth() / 2;
final int npy = posCircleY - posCircle.getHeight() / 2;
posCircle.move(npx, npy);
// move other MapElements
updatePositionOfMapElements();
repaint();
}
/**
* zommes in if w>0 and out if w<0
*
* @param firstclickpoint
* @param w
* @param h
*/
public void zoomScreenRect(Point firstclickpoint, int w, int h) {
// maximal size of the zoomed image
// don't make this too big, otherwise it causes out of memory errors
int newImageWidth = (int) (this.width * (this.width < 481 ? 2 : 1.6));
int newImageHeight = (int) (this.height * (this.width < 481 ? 2 : 1.6));
final CWPoint center = ScreenXY2LatLon(firstclickpoint.x + w / 2, firstclickpoint.y + h / 2);
float zoomFactor;
if (h < 0) {
h = java.lang.Math.abs(h);
firstclickpoint.y = firstclickpoint.y - h;
}
if (w > 0) { // zoom in
zoomFactor = java.lang.Math.min((float) this.width / (float) w, (float) this.height / (float) h);
} else { // zoom out
w = java.lang.Math.abs(w);
// make firstclickedpoint the upper left corner
firstclickpoint.x = firstclickpoint.x - w;
zoomFactor = java.lang.Math.max((float) w / (float) this.width, (float) h / (float) this.height);
}
// calculate rect in unzoomed image in a way that the centre of the
// new image is the centre of selected area but give priority to the
// prefered image size of the scaled image
newImageHeight = (int) (newImageHeight / zoomFactor / currentMap.zoomFactor);
newImageWidth = (int) (newImageWidth / zoomFactor / currentMap.zoomFactor);
final Point mappos = getMapPositionOnScreen();
final int xinunscaledimage = (int) ((firstclickpoint.x - mappos.x + w / 2) / currentMap.zoomFactor + currentMap.shift.x - newImageWidth / 2);
final int yinunscaledimage = (int) ((firstclickpoint.y - mappos.y + h / 2) / currentMap.zoomFactor + currentMap.shift.y - newImageHeight / 2);
final Rect newImageRect = new Rect(xinunscaledimage, yinunscaledimage, newImageWidth, newImageHeight);
if (mapImage1to1 != null && mmp.getMainMap() != null && mapImage1to1.image != null) {
// try to avoid overlapping by shifting
if (newImageRect.x < 0)
newImageRect.x = 0; // align left if left overlapping
if (newImageRect.y < 0)
newImageRect.y = 0;
// align right if right overlapping
if (newImageRect.x + newImageRect.width >= mapImage1to1.getWidth())
newImageRect.x = mapImage1to1.getWidth() - newImageWidth;
if (newImageRect.y + newImageRect.height >= mapImage1to1.getHeight())
newImageRect.y = mapImage1to1.getHeight() - newImageHeight;
// crop if after shifting still overlapping
if (newImageRect.x < 0)
newImageRect.x = 0;
if (newImageRect.y < 0)
newImageRect.y = 0;
if (newImageRect.x + newImageRect.width >= mapImage1to1.getWidth())
newImageRect.width = mapImage1to1.getWidth() - newImageRect.x;
if (newImageRect.y + newImageRect.height >= mapImage1to1.getHeight())
newImageRect.height = mapImage1to1.getHeight() - newImageRect.y;
}
zoomFromUnscaled(zoomFactor * currentMap.zoomFactor, newImageRect, center);
}
/**
* do the actual scaling
*
* @param zoomFactor
* relative to original image
* @param newImageRect
* Rect in the 1:1 image that contains the area to be zoomed into
* @param center
*/
public void zoomFromUnscaled(float zoomFactor, Rect newImageRect, CWPoint center) {
Vm.showWait(this, true);
final boolean remember = dontUpdatePos;
if (mapImage1to1 != null) {
dontUpdatePos = true; // avoid multi-thread problems
int saveprop = mImage.IsMoveable;
// remove + destroy the zoomed
MapImage mainMap = mmp.getMainMap();
if (mainMap != null) {
saveprop = mainMap.properties;
mmp.removeImage(mainMap);
if (mainMap != mapImage1to1) {
// gezoomt
mainMap.free();
mmp.setMainMap(null);
mainMap = null;
}
}
// do garbage collection
Vm.getUsedMemory(true);
mainMap = mapImage1to1;
try {
if (zoomFactor != 1)
mainMap = new MapImage(mapImage1to1.scale((int) (newImageRect.width * zoomFactor), (int) (newImageRect.height * zoomFactor), newImageRect, 0));
currentMap.zoom(zoomFactor, newImageRect.x, newImageRect.y);
} catch (final OutOfMemoryError e) {
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4222, "Out of memory error")).wait(FormBase.OKB);
}
// do garbage collection
Vm.getUsedMemory(true);
mmp.setMainMap(mainMap);
mainMap.properties = saveprop;
if (mapHidden)
mainMap.hide();
mmp.addImage(mainMap);
mmp.images.moveToBack(mainMap);
if (mapImage1to1 != null && mainMap != null && mapImage1to1.image != null) {
final Point mappos = getMapPositionOnScreen();
mainMap.move(mappos.x, mappos.y);
}
} else // no map image loaded
{
currentMap.zoom(zoomFactor, newImageRect.x, newImageRect.y);
}
// scaleWanted = currentMap.scale; use this if you want to change
// automatically to a map scale that best fits the zooming
destroyOverlaySet();
Vm.getUsedMemory(true); // call garbage collection
setCenterOfScreen(center, false);
addOverlaySet();
updateScale();
this.repaintNow();
Vm.showWait(this, false);
dontUpdatePos = remember;
}
public void onEvent(Event ev) {
if (ev instanceof FormEvent && (ev.type == FormEvent.CLOSED)) {
Preferences.itself().lastScale = currentMap.scale;
running = false;
}
if (ev instanceof KeyEvent && ev.target == this && ((((KeyEvent) ev).key == IKeys.ESCAPE) || (((KeyEvent) ev).key == IKeys.ENTER) || (((KeyEvent) ev).key == IKeys.ACTION))) {
this.close(0);
ev.consumed = true;
}
super.onEvent(ev);
}
public boolean handleCommand(int actionCommand) {
if (CLOSE == actionCommand) {
final WindowEvent tmp = new WindowEvent();
tmp.type = WindowEvent.CLOSE;
postEvent(tmp);
return true;
}
if (SELECT_MAP == actionCommand) {
manualSelectMap();
return true;
}
if (CHANGE_MAP_DIR == actionCommand) {
final FileChooser fc = new FileChooser(FileChooserBase.DIRECTORY_SELECT, MainForm.profile.getMapsDir());
fc.addMask("*.wfl");
fc.setTitle(MyLocale.getMsg(4200, "Select map directory:"));
if (fc.execute() != FormBase.IDCANCEL) {
// todo : perhaps only a temporary change
MainForm.profile.setRelativeMapsDir(MainForm.profile.getMapsSubDir(fc.getChosen().toString()));
mapsloaded = false;
forceMapLoad = true;
initMaps(posCircle.where);
}
return true;
}
if (FILL_MAP == actionCommand) {
setFillWhiteArea(true);
return true;
}
if (NO_FILL_MAP == actionCommand) {
setFillWhiteArea(false);
return true;
}
if (SHOW_CACHES == actionCommand) {
setShowCachesOnMap(true);
return true;
}
if (HIDE_CACHES == actionCommand) {
setShowCachesOnMap(false);
return true;
}
if (HIDE_MAP == actionCommand) {
mmp.hideMap();
mapHidden = true;
repaintNow();
return true;
}
if (SHOW_MAP == actionCommand) {
mmp.showMap();
mapHidden = false;
repaintNow();
return true;
}
if (HIGHEST_RES_GPS_DEST == actionCommand) {
setResModus(MovingMap.HIGHEST_RESOLUTION_GPS_DEST);
return true;
}
if (HIGHEST_RES == actionCommand) {
setResModus(MovingMap.HIGHEST_RESOLUTION);
return true;
}
if (KEEP_MAN_RESOLUTION == actionCommand) {
setResModus(MovingMap.NORMAL_KEEP_RESOLUTION);
return true;
}
if (MORE_DETAILS == actionCommand) {
loadMoreDetailedMap(false);
return true;
}
if (MORE_OVERVIEW == actionCommand) {
loadMoreDetailedMap(true);
return true;
}
if (ALL_CACHES_RES == actionCommand) {
loadMapForAllCaches();
return true;
}
if (MOVE_TO_CENTER == actionCommand) {
setCenterOfScreen(Preferences.itself().curCentrePt, true);
return true;
}
if (MOVE_TO_DEST == actionCommand) {
if (gotoPos != null) {
setCenterOfScreen(gotoPos.where, true);
}
return true;
}
if (MOVE_TO_GPS == actionCommand) {
MainTab.itself.navigate.startGps(Preferences.itself().logGPS, Convert.toInt(Preferences.itself().logGPSTimer));
SnapToGps();
return true;
}
if (ZOOM_1_TO_1 == actionCommand) {
zoom1to1();
return true;
}
if (ZOOMIN == actionCommand) {
zoomin();
return true;
}
if (ZOOMOUT == actionCommand) {
zoomout();
return true;
}
return controlsLayer.handleCommand(actionCommand);
}
private void manualSelectMap() {
CWPoint gpspos;
if (Navigate.gpsPos.Fix > 0)
gpspos = new CWPoint(Navigate.gpsPos.latDec, Navigate.gpsPos.lonDec);
else
gpspos = null;
final ListBox manualSelectMap = new ListBox(maps, gpspos, getGotoPosWhere(), currentMap);
if (manualSelectMap.execute() == FormBase.IDOK) {
autoSelectMap = false;
if (manualSelectMap.selectedMap.isInBound(posCircle.where) || manualSelectMap.selectedMap.getImagePathAndName().length() == 0) {
setMap(manualSelectMap.selectedMap, posCircle.where);
setResModus(MovingMap.NORMAL_KEEP_RESOLUTION);
ignoreGps = false;
} else {
setGpsStatus(MovingMap.noGPS);
ignoreGps = true;
setMap(manualSelectMap.selectedMap, posCircle.where);
// if map has an image
if (currentMap.getMapType() != 1)
setCenterOfScreen(manualSelectMap.selectedMap.center, true);
setResModus(MovingMap.NORMAL_KEEP_RESOLUTION);
}
}
}
private void initMaps(CWPoint where) {
loadMaps(where.latDec);
lastCompareX = Integer.MAX_VALUE;
lastCompareY = Integer.MAX_VALUE;
autoSelectMap = true;
setBestMap(where, true);
forceMapLoad = false;
}
private void setFillWhiteArea(boolean fillWhiteArea) {
if (Preferences.itself().fillWhiteArea != fillWhiteArea) {
Preferences.itself().fillWhiteArea = fillWhiteArea;
if (!fillWhiteArea)
mmp.clearMapTiles();
else {
forceMapLoad = true;
updatePosition(posCircle.where);
}
}
}
private void setShowCachesOnMap(boolean value) {
if (value != Preferences.itself().showCachesOnMap) {
Preferences.itself().showCachesOnMap = value;
updatePositionOfMapElements();
}
}
private void setResModus(int modus) {
scaleWanted = currentMap.scale;
if (mapChangeModus == modus)
return;
mapChangeModus = modus;
lastHighestResolutionGPSDestScale = -1;
if (modus != NORMAL_KEEP_RESOLUTION) {
setBestMap(posCircle.where, true);
}
}
/**
*
* @param betterOverview
* true: getmap with better overview
* @return
*/
private void loadMoreDetailedMap(boolean betterOverview) {
// width == 0 happens if this routine is run before the windows is on the screen
final int w = (width != 0 ? width : Preferences.itself().getScreenWidth());
final int h = (height != 0 ? height : Preferences.itself().getScreenHeight());
final Rect screen = new Rect(0, 0, w, h);
CWPoint cll;
if (currentMap != null) {
cll = ScreenXY2LatLon(w / 2, h / 2);
} else {
cll = new CWPoint(posCircle.where);
}
final MapInfoObject m = maps.getMapChangeResolution(cll, screen, currentMap.scale * currentMap.zoomFactor, !betterOverview);
if (m != null) {
final boolean remember = dontUpdatePos;
dontUpdatePos = true;
setMap(m, cll);
setResModus(MovingMap.NORMAL_KEEP_RESOLUTION);
if (isFillWhiteArea()) {
fillWhiteArea();
}
dontUpdatePos = remember;
} else
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4211, "No ") + (betterOverview ? MyLocale.getMsg(4212, "less") : MyLocale.getMsg(4213, "more")) + MyLocale.getMsg(4214, " detailed map available")).wait(FormBase.OKB);
}
private void loadMapForAllCaches() {
final BoundingBox sur = MainForm.profile.getSourroundingArea(true);
if (sur == null) {
new InfoBox(MyLocale.getMsg(5500, "Error"), MyLocale.getMsg(4215, "Keine Caches mit H?ckchen ausgew?hlt")).wait(FormBase.OKB);
return;
}
MapInfoObject newmap = maps.getMapForArea(sur.topleft, sur.bottomright);
if (newmap == null) { // no map that includs all caches is
// available -> load map with lowest
// resolution
final Object[] s = getRectForMapChange(posCircle.where);
final CWPoint cll = (CWPoint) s[0];
final Rect screen = (Rect) s[1];
newmap = maps.getBest(cll, screen, Float.MAX_VALUE - 1, false, true);
}
if (newmap == null) { // no map is covering any area of the caches
final Object[] s = getRectForMapChange(posCircle.where);
// CWPoint cll = (CWPoint) s[0];
// for the size of the cache image
final Rect screen = (Rect) s[1];
final float neededscalex = (float) (sur.topleft.getDistance(sur.topleft.latDec, sur.bottomright.lonDec) * 1000 / (screen.width - 15));
final float neededscaley = (float) (sur.topleft.getDistance(sur.bottomright.latDec, sur.topleft.lonDec) * 1000 / (screen.height - 15));
// beware: "-4" only works if the empty maps were added last (see MapsList.addEmptyMaps)
newmap = ((MapListEntry) maps.elementAt(maps.getCount() - 4)).getMap();
newmap.zoom(newmap.scale * newmap.zoomFactor / (neededscalex > neededscaley ? neededscalex : neededscaley), 0, 0);
forceMapLoad = true;
}
final boolean remember = dontUpdatePos;
dontUpdatePos = true;
setMap(newmap, posCircle.where);
setResModus(MovingMap.NORMAL_KEEP_RESOLUTION);
dontUpdatePos = remember;
}
private void setCenterOfScreen(CWPoint c, boolean neuZeichnen) {
moveScreenXYtoLatLon(new Point(this.width / 2, this.height / 2), c, neuZeichnen);
}
private void SnapToGps() {
resetCenterOfMap();
dontUpdatePos = false;
ignoreGps = false;
lastCompareX = Integer.MAX_VALUE; // neccessary to make
// updateposition to test if the
// current map is the best one
// for the GPS-Position
lastCompareY = Integer.MAX_VALUE;
autoSelectMap = true;
forceMapLoad = true;
// showMap(); why this?
if (Navigate.gpsPos.Fix <= 0)
updatePosition(posCircle.where);
else
updatePositionFromGps(Navigate.gpsPos.getFix());
}
private void zoom1to1() {
final CWPoint center = ScreenXY2LatLon(this.width / 2, this.height / 2);
if (mapImage1to1 != null)
zoomFromUnscaled(1, new Rect(0, 0, mapImage1to1.getWidth(), mapImage1to1.getHeight()), center);
else
zoomFromUnscaled(1, new Rect(0, 0, 1, 1), center);
}
private void zoomin() {
zoomScreenRect(new Point(this.width / 4, this.height / 4), this.width / 2, this.height / 2);
}
private void zoomout() {
final CWPoint center = currentMap.center;
float zoomfactor = currentMap.zoomFactor / 2;
if (zoomfactor < 1) {
zoomfactor = 1;
}
if (mapImage1to1 != null)
zoomFromUnscaled(zoomfactor, new Rect(0, 0, mapImage1to1.getWidth(), mapImage1to1.getHeight()), center);
else
zoomFromUnscaled(zoomfactor, new Rect(0, 0, 1, 1), center);
}
public MapSymbol getDestination() {
return gotoPos;
}
/**
* @return the dontUpdatePos
*/
public boolean dontUpdatePos() {
return dontUpdatePos;
}
/**
* @param dontUpdatePos
* the dontUpdatePos to set
*/
public void setDontUpdatePos(boolean dontUpdatePos) {
this.dontUpdatePos = dontUpdatePos;
}
/**
* @return the zoomingMode
*/
public boolean isZoomingMode() {
return zoomingMode;
}
/**
* @param zoomingMode
* the zoomingMode to set
*/
public void setZoomingMode(boolean zoomingMode) {
this.zoomingMode = zoomingMode;
}
/**
* @param mapsloaded
* the mapsloaded to set
*/
public void setMapsloaded(boolean mapsloaded) {
this.mapsloaded = mapsloaded;
}
/**
* @return the paintPosDestLine
*/
public boolean doPaintPosDestLine() {
return doPaintPosDestLine;
}
/**
* @param doPaintPosDestLine
* the doPaintPosDestLine to set
*/
public void setPaintPosDestLine(boolean doPaintPosDestLine) {
this.doPaintPosDestLine = doPaintPosDestLine;
}
/**
* @return the mobileVGA
*/
public boolean isMobileVGA() {
return mobileVGA;
}
public boolean isFillWhiteArea() {
return Preferences.itself().fillWhiteArea;
}
public void addImage(AniImage ani) {
mmp.addImage(ani);
}
public void removeImage(AniImage ani) {
mmp.removeImage(ani);
}
/**
* @return the posCircleX
*/
public int getPosCircleX() {
return posCircleX;
}
/**
* @return the posCircleY
*/
public int getPosCircleY() {
return posCircleY;
}
}
/**
* Class to display the map bitmap and to select another bitmap to display.
*/
class MovingMapPanel extends InteractivePanel implements EventListener {
Menu kontextMenu;
MenuItem gotoMenuItem = new MenuItem(MyLocale.getMsg(4230, "Goto here$g"), 0, null);
MenuItem newWayPointMenuItem = new MenuItem(MyLocale.getMsg(4232, "Create new Waypoint here$n"), 0, null);;
MenuItem openCacheDescMenuItem, openCacheDetailMenuItem, addCachetoListMenuItem, gotoCacheMenuItem, markFoundMenuItem, hintMenuItem, missionMenuItem;
MenuItem miLuminary[];
CacheHolder clickedCache;
private MovingMap mm;
private MapImage mainMap;
private Vector mapTiles; // to remember the additional Tiles
private Point saveMapLoc = null;
private boolean remember;
private boolean paintingZoomArea;
private ImageList saveImageList = null;
private int lastZoomWidth, lastZoomHeight;
private boolean ignoreNextDrag = false; // for handling a misfired drag event on pda
private boolean onlyIfCache = false;
// ctor
public MovingMapPanel(MovingMap mm) {
this.mm = mm;
miLuminary = new MenuItem[SkyOrientation.LUMINARY_NAMES.length];
for (int i = 0; i < SkyOrientation.LUMINARY_NAMES.length; i++) {
miLuminary[i] = new MenuItem(SkyOrientation.getLuminaryName(i));
}
// want to get simulated right-clicks
set(ControlConstants.WantHoldDown, true);
mapTiles = new Vector();
}
//Overrides: imageBeginDragged(...) in InteractivePanel
public boolean imageBeginDragged(AniImage which, Point pos) {
if (mm.isZoomingMode() == true) {
return false;
}
// drag at mapcontrols doesn't drag the map
if (mm.getControlsLayer().imageBeginDragged(which, pos)) {
return false;
}
// move (drag) map
remember = mm.dontUpdatePos();
mm.setDontUpdatePos(true);
saveMapLoc = pos;
bringMapToTop();
if (mainMap.isOnScreen() && !mainMap.hidden)
return super.imageBeginDragged(mainMap, pos);
else
return super.imageBeginDragged(null, pos);
}
//Overrides: imageNotDragged(...) in InteractivePanel
public boolean imageNotDragged(ImageDragContext dc, Point pos) {
final boolean ret = super.imageNotDragged(dc, pos);
bringMaptoBack();
if (dc.image == null)
moveMap(pos.x - saveMapLoc.x, pos.y - saveMapLoc.y);
else
mapMoved(pos.x - saveMapLoc.x, pos.y - saveMapLoc.y);
mm.setDontUpdatePos(remember);
return ret;
}
//Overrides onPenEvent(...) in MosaicPanel
public void onPenEvent(PenEvent ev) {
if (ignoreNextDrag) {
// On PDA next event after a Kontext ist a drag, that will move the map unwanted
ignoreNextDrag = false;
if (ev.type == PenEvent.PEN_DRAG)
return; // ignoring now
}
if (ev.type == PenEvent.PEN_DOWN) {
if (mm.isZoomingMode()) {
remember = mm.dontUpdatePos();
mm.setDontUpdatePos(true);
saveMapLoc = new Point(ev.x, ev.y);
paintingZoomArea = true;
mm.setZoomingMode(true);
} else {
saveMapLoc = new Point(ev.x, ev.y);
if (ev.modifiers == PenEvent.RIGHT_BUTTON) {
// context penHeld is fired directly on PDA (cause WantHoldDown Control Modifier) but not on PC (Java)
penHeld(new Point(ev.x, ev.y));
} else {
// do it even on left klick
onlyIfCache = true;
penHeld(new Point(ev.x, ev.y));
}
}
} else {
if (mm.isZoomingMode()) {
if (ev.type == PenEvent.PEN_UP) {
paintingZoomArea = false;
mm.setZoomingMode(false);
mm.getControlsLayer().changeRoleState("zoom_manually", false);
mm.setDontUpdatePos(remember);
// dont make to big zoom jumps - it is most probable not an intentional zoom
if (java.lang.Math.abs(lastZoomWidth) < 15 || java.lang.Math.abs(lastZoomHeight) < 15) {
repaintNow();
return;
}
mm.zoomScreenRect(saveMapLoc, lastZoomWidth, lastZoomHeight);
}
if (paintingZoomArea && (ev.type == PenEvent.PEN_MOVED_ON || ev.type == PenEvent.PEN_MOVE || ev.type == PenEvent.PEN_DRAG)) {
int left, top;
final Graphics dr = this.getGraphics();
if (lastZoomWidth < 0)
left = saveMapLoc.x + lastZoomWidth;
else
left = saveMapLoc.x;
if (lastZoomHeight < 0)
top = saveMapLoc.y + lastZoomHeight;
else
top = saveMapLoc.y;
left -= 2;
top -= 2;
if (top < 0)
top = 0;
if (left < 0)
left = 0;
this.repaintNow(dr, new Rect(left, top, java.lang.Math.abs(lastZoomWidth) + 4, java.lang.Math.abs(lastZoomHeight) + 4));
lastZoomWidth = ev.x - saveMapLoc.x;
lastZoomHeight = ev.y - saveMapLoc.y;
if (lastZoomWidth < 0)
left = saveMapLoc.x + lastZoomWidth;
else
left = saveMapLoc.x;
if (lastZoomHeight < 0)
top = saveMapLoc.y + lastZoomHeight;
else
top = saveMapLoc.y;
dr.setPen(new Pen(new Color(255, 0, 0), Pen.SOLID, 3));
// bug in ewe: thickness parameter is ignored
dr.drawRect(left, top, java.lang.Math.abs(lastZoomWidth), java.lang.Math.abs(lastZoomHeight), 0);
}
}
}
super.onPenEvent(ev);
}
//Overrides: doPaint(...) in Mosaic
public void doPaint(Graphics g, Rect area) {
super.doPaint(g, area);
if (mm.getGotoPosWhere() != null && mm.doPaintPosDestLine()) {
final Point dest = mm.getXYonScreen(mm.getGotoPosWhere());
g.setPen(new Pen(Color.DarkBlue, Pen.SOLID, 3));
g.drawLine(mm.getPosCircleX(), mm.getPosCircleY(), dest.x, dest.y);
}
}
/**
* @return MapImage the mapImage
*/
public MapImage getMainMap() {
return mainMap;
}
public boolean ScreenCompletlyCoveredByMainMap(int ScreenWidth, int ScreenHeight) {
if (mainMap != null) {
Point mapPos = mainMap.locAlways;
return !(mapPos.x > 0 || mapPos.y > 0 || mapPos.x + mainMap.getWidth() < ScreenWidth || mapPos.y + mainMap.getHeight() < ScreenHeight);
} else
return false;
}
/**
* @param mapImage
* the mapImage to set
*/
public void setMainMap(MapImage mainMap) {
this.mainMap = mainMap;
}
// remove all images from screen except the main mapImage. You con visibly drag only one image
private void bringMapToTop() {
if (mainMap == null || mainMap.hidden) {
saveImageList = null;
return;
}
saveImageList = new ImageList();
saveImageList.copyFrom(images);
images.removeAllElements();
images.add(mainMap);
}
private void bringMaptoBack() {
if (saveImageList == null)
return;
images = saveImageList;
saveImageList = null;
}
public void moveMainMapImage(int x, int y) {
if (mainMap != null) {
mainMap.move(x, y);
}
}
public void moveMap(int diffX, int diffY) {
if (diffX == 0 && diffY == 0)
return;
Point p = new Point();
if (mainMap != null) {
p = mainMap.locAlways;
mainMap.move(p.x + diffX, p.y + diffY);
}
mapMoved(diffX, diffY);
}
public void mapMoved(int diffX, int diffY) {
mm.mapMoved(diffX, diffY);
this.repaintNow();
}
public void addMapTile(MapImage mapTile) {
if (!mapTiles.contains(mapTile)) {
mapTiles.add(mapTile);
addImage(mapTile);
}
}
public void removeMapTile(MapImage mapTile) {
mapTiles.remove(mapTile);
removeImage(mapTile);
}
public void clearMapTiles() {
for (int i = mapTiles.size() - 1; i >= 0; i--) {
removeImage((AniImage) mapTiles.get(i));
}
mapTiles.clear();
}
public void updatePositionOfMapTiles(int deltaX, int deltaY) {
if (deltaX == 0 && deltaY == 0)
return;
for (int i = mapTiles.size() - 1; i >= 0; i--) {
MapImage mi = (MapImage) mapTiles.get(i);
mi.setLocation(mi.locAlways.x + deltaX, mi.locAlways.y + deltaY);
}
}
public void hideMap() {
mainMap.hide();
for (int i = mapTiles.size() - 1; i >= 0; i--) {
((MapImage) mapTiles.get(i)).hide();
}
}
public void showMap() {
mainMap.unhide();
for (int i = mapTiles.size() - 1; i >= 0; i--) {
((MapImage) mapTiles.get(i)).unhide();
}
}
/**
* Method to react to user.
*/
public void imageClicked(AniImage which, Point pos) {
mm.getControlsLayer().imageClicked(which);
}
public void penHeld(Point p) {
ignoreNextDrag = true;
if (!mm.isZoomingMode()) {
kontextMenu = new Menu();
if (!onlyIfCache) {
kontextMenu.addItem(gotoMenuItem);
kontextMenu.addItem(newWayPointMenuItem);
kontextMenu.addItem(new MenuItem("-"));
}
final AniImage clickedOnImage = images.findHotImage(p);
if (clickedOnImage != null && clickedOnImage instanceof MapSymbol) {
if (((MapSymbol) clickedOnImage).mapObject instanceof CacheHolder) {
clickedCache = (CacheHolder) (((MapSymbol) clickedOnImage).mapObject);
// clickedCache == null can happen if clicked on the
// goto-symbol
if (clickedCache != null) {
CacheHolder ch = clickedCache;
if (clickedCache.isAddiWpt()) {
ch = clickedCache.mainCache;
}
if (ch != null) {
kontextMenu.addItem(new MenuItem(ch.getCode() + " '" + ch.getName() + "'"));
if (!ch.isCustomWpt()) {
kontextMenu.addItem(new MenuItem( //
CacheSize.cw2ExportString(ch.getSize()) + //
" D: " + CacheTerrDiff.longDT(ch.getDifficulty()) + //
" T: " + CacheTerrDiff.longDT(ch.getTerrain()) + //
""));
kontextMenu.addItem(new MenuItem( //
"" + ch.getOwner() + //
" " + ch.getHidden() + //
""));
}
}
if (clickedCache.isAddiWpt()) {
kontextMenu.addItem(new MenuItem(clickedCache.getCode() + " '" + clickedCache.getName() + "'"));
}
kontextMenu.addItem(new MenuItem("-"));
openCacheDescMenuItem = new MenuItem(MyLocale.getMsg(201, "Open Desctiption") + "$o");
kontextMenu.addItem(openCacheDescMenuItem);
openCacheDetailMenuItem = new MenuItem(MyLocale.getMsg(200, "Open Details") + "$e");
kontextMenu.addItem(openCacheDetailMenuItem);
gotoCacheMenuItem = new MenuItem(MyLocale.getMsg(4279, "Goto") + "$g");
kontextMenu.addItem(gotoCacheMenuItem);
if (!clickedCache.isFound()) {
int msgNr = CacheType.getLogMsgNr(clickedCache.getType());
markFoundMenuItem = new MenuItem(MyLocale.getMsg(msgNr, "Found") + "$m");
kontextMenu.addItem(markFoundMenuItem);
}
if (MainForm.itself.cacheTourVisible) {
addCachetoListMenuItem = new MenuItem(MyLocale.getMsg(199, "Add to cachetour"));
kontextMenu.addItem(addCachetoListMenuItem);
}
String stmp = clickedCache.getDetails().Hints;
stmp = stmp.substring(0, Math.min(10, stmp.length())).trim();
if (!stmp.equals("")) {
kontextMenu.addItem(hintMenuItem = new MenuItem("Hint: " + stmp));
}
if (clickedCache.getType() == CacheType.CW_TYPE_QUESTION) {
stmp = clickedCache.getDetails().LongDescription;
if (!stmp.equals("")) {
kontextMenu.addItem(missionMenuItem = new MenuItem("?: "));
}
}
}
}
}
/*
* this kontext will be replaced by the settings of the rose in the goto panel
*
* if ( !(mm.directionArrows.onHotArea(p.x, p.y)) ) { } else { for (int i=0; i<SkyOrientation.LUMINARY_NAMES.length; i++) { kontextMenu.addItem(miLuminary[i]); if (i == mm.MainTab.itself.navigate.luminary) miLuminary[i].modifiers |=
* MenuItem.Checked; else miLuminary[i].modifiers &= MenuItem.Checked; } }
*/
onlyIfCache = false;
if (kontextMenu.items.size() > 0) {
kontextMenu.exec(this, new Point(p.x, p.y), this);
} else
kontextMenu = null;
}
}
public boolean imageMovedOn(AniImage which) {
if (which instanceof MapSymbol) {
if (((MapSymbol) which).mapObject instanceof CacheHolder) {
final CacheHolder ch = (CacheHolder) ((MapSymbol) which).mapObject;
this.toolTip = ch.getCode() + "\n" + ch.getName();
}
}
return true;
}
public boolean imageMovedOff(AniImage which) {
if (which instanceof MapSymbol) {
if (((MapSymbol) which).mapObject instanceof CacheHolder) {
this.toolTip = null;
}
}
return true;
}
public void onEvent(Event ev) {
// nothing selected in kontext
if (kontextMenu != null && ev instanceof PenEvent && ev.type == PenEvent.PEN_DOWN && ev.target == this) {
kontextMenu.close();
kontextMenu = null;
return;
}
// something selected
if (ev instanceof MenuEvent) {
if (ev.target == kontextMenu) {
if ((((MenuEvent) ev).type == MenuEvent.SELECTED)) {
final MenuItem action = (MenuItem) kontextMenu.getSelectedItem();
if (action == gotoMenuItem) {
closeKontextMenu();
MainTab.itself.navigate.setDestination(mm.ScreenXY2LatLon(saveMapLoc.x, saveMapLoc.y));
}
if (action == openCacheDescMenuItem || action == openCacheDetailMenuItem) {
leaveMovingMap();
final MainTab mainT = MainTab.itself;
if (action == openCacheDescMenuItem)
mainT.openPanel(MainTab.MAP_CARD, clickedCache.getCode(), MainTab.DESCRIPTION_CARD);
else
mainT.openPanel(MainTab.MAP_CARD, clickedCache.getCode(), MainTab.DETAILS_CARD);
}
if (action == gotoCacheMenuItem) {
closeKontextMenu();
MainTab.itself.navigate.setDestination(clickedCache);
}
if (action == markFoundMenuItem) {
closeKontextMenu();
final Time dtm = new Time();
dtm.setFormat("yyyy-MM-dd HH:mm");
clickedCache.setStatus(dtm.toString());
clickedCache.setFound(true);
clickedCache.saveCacheDetails();
if (clickedCache.hasAddiWpt()) {
CacheHolder addiWpt;
for (int i = clickedCache.addiWpts.getCount() - 1; i >= 0; i--) {
addiWpt = (CacheHolder) clickedCache.addiWpts.get(i);
addiWpt.setStatus(dtm.toString());
addiWpt.setFound(true);
addiWpt.saveCacheDetails();
mm.removeMapSymbol(addiWpt.getCode());
}
}
mm.removeMapSymbol(clickedCache.getCode());
mm.updateSymbolPositions();
this.repaintNow();
}
if (action == newWayPointMenuItem) {
leaveMovingMap();
final CacheHolder newWP = new CacheHolder();
newWP.setWpt(mm.ScreenXY2LatLon(saveMapLoc.x, saveMapLoc.y));
MainTab.itself.newWaypoint(newWP);
}
if (action == addCachetoListMenuItem) {
closeKontextMenu();
MainForm.itself.addCache(clickedCache.getCode());
}
if (action == hintMenuItem) {
new InfoBox("Hint", STRreplace.replace(Common.rot13(clickedCache.getDetails().Hints), "<br>", "\n")).wait(FormBase.OKB);
}
if (action == missionMenuItem) {
new InfoBox("Mission", STRreplace.replace(clickedCache.getDetails().LongDescription, "<br>", "\n")).wait(FormBase.OKB);
}
/*
* for (int i=0; i<miLuminary.length; i++) { if (action == miLuminary[i]) { kontextMenu.close(); mm.MainTab.itself.navigate.setLuminary(i); mm.updateGps(mm.MainTab.itself.navigate.gpsPos.getFix()); miLuminary[i].modifiers |=
* MenuItem.Checked; } else miLuminary[i].modifiers &= ~MenuItem.Checked; }
*/
}
} // if (ev.target == kontextMenu)
}
super.onEvent(ev);
}
private void closeKontextMenu() {
kontextMenu.close();
// for not to do an additional klick (before reacting on klicks)
final PenEvent pev = new PenEvent();
pev.target = this;
pev.type = PenEvent.PEN_DOWN;
this.postEvent(pev);
pev.type = PenEvent.PEN_UP;
this.postEvent(pev);
}
private void leaveMovingMap() {
closeKontextMenu();
final WindowEvent close = new WindowEvent();
close.target = this;
close.type = WindowEvent.CLOSE;
this.postEvent(close);
}
}
/**
* Class to display maps to choose from
*/
class ListBox extends Form {
public MapInfoObject selectedMap;
private final ExecutePanel executePanel;
mList list = new mList(4, 1, false);
public boolean selected = false;
private Vector maps;
public ListBox(Vector maps, CWPoint Gps, CWPoint gotopos, MapInfoObject curMap) {
this.title = MyLocale.getMsg(4271, "Maps");
// if (Gui.screenIs(Gui.PDA_SCREEN)) this.setPreferredSize(200,100);
// else
// set width to screenwidth *3/4 but to at least 240 if the screen
// is big engough for 240px width
this.setPreferredSize(java.lang.Math.max(Preferences.itself().getScreenWidth() * 3 / 4, java.lang.Math.min(240, Preferences.itself().getScreenWidth())), Preferences.itself().getScreenHeight() * 3 / 4);
this.maps = maps;
MapInfoObject mio;
MapListEntry ml;
String cmp;
int oldmap = -1;
boolean curMapFound = false;
final boolean[] inList = new boolean[maps.size()];
int row = -1;
if (curMap == null)
curMapFound = true;
if (gotopos != null && Gps != null) {
list.addItem(MyLocale.getMsg(4272, "--- Maps containing GPS and goto pos. ---"));
row++;
cmp = "FF1" + (new BoundingBox(new CWPoint(Gps.latDec, Gps.lonDec), gotopos)).getEasyFindString();
for (int i = 0; i < maps.size(); i++) {
ml = (MapListEntry) maps.get(i);
if (!BoundingBox.containsRoughly(ml.sortEntryBBox, cmp))
continue; // TODO if no map available
else {
mio = ml.getMap();
}
if (mio.isInBound(Gps.latDec, Gps.lonDec) && mio.isInBound(gotopos)) {
list.addItem(i + ": " + mio.getMapNameForList());
row++;
inList[i] = true;
if (!curMapFound && curMap != null && mio.getMapNameForList().equals(curMap.getMapNameForList())) {
oldmap = row;
curMapFound = true;
}
} else
inList[i] = false;
}
}
if (Gps != null) {
list.addItem(MyLocale.getMsg(4273, "--- Maps containing curr. position ---"));
row++;
cmp = "FF1" + BoundingBox.getEasyFindString(new CWPoint(Gps.latDec, Gps.lonDec), 30);
for (int i = 0; i < maps.size(); i++) {
ml = (MapListEntry) maps.get(i);
if (!BoundingBox.containsRoughly(ml.sortEntryBBox, cmp))
continue; // TODO if no map available
else {
mio = ml.getMap();
}
if (mio.isInBound(Gps.latDec, Gps.lonDec)) {
list.addItem(i + ": " + mio.getMapNameForList());
row++;
inList[i] = true;
if (!curMapFound && curMap != null && mio.getMapNameForList().equals(curMap.getMapNameForList())) {
oldmap = row;
curMapFound = true;
}
}
}
}
if (gotopos != null) {
list.addItem(MyLocale.getMsg(4274, "--- Karten des Ziels ---"));
row++;
cmp = "FF1" + BoundingBox.getEasyFindString(gotopos, 30);
for (int i = 0; i < maps.size(); i++) {
ml = (MapListEntry) maps.get(i);
if (!BoundingBox.containsRoughly(ml.sortEntryBBox, cmp))
continue; // TODO if no map available
else {
mio = ml.getMap();
}
if (mio.isInBound(gotopos)) {
list.addItem(i + ": " + mio.getMapNameForList());
row++;
inList[i] = true;
if (!curMapFound && curMap != null && mio.getMapNameForList().equals(curMap.getMapNameForList())) {
oldmap = row;
curMapFound = true;
}
}
}
}
list.addItem(MyLocale.getMsg(4275, "--- andere Karten ---"));
row++;
for (int i = 0; i < maps.size(); i++) {
ml = (MapListEntry) maps.get(i);
if (!inList[i]) {
list.addItem(i + ": " + ml.getMapNameForList());
row++;
if (!curMapFound && curMap != null && ml.getMapNameForList().equals(curMap.getMapNameForList())) {
oldmap = row;
curMapFound = true;
}
}
}
list.selectItem(oldmap, true);
this.addLast(new MyScrollBarPanel(list), CellConstants.STRETCH, CellConstants.FILL);
executePanel = new ExecutePanel(this);
executePanel.applyButton.takeFocus(0);
}
public void mapSelected() {
try {
selectedMap = null;
int mapNum = 0;
String it = list.getText();
if (it.length() > 0) {
it = it.substring(0, it.indexOf(':'));
mapNum = Convert.toInt(it);
selectedMap = ((MapListEntry) maps.get(mapNum)).getMap();
selected = true;
this.close(FormBase.IDOK);
} else {
selected = false;
this.close(FormBase.IDCANCEL);
}
} catch (final NegativeArraySizeException e) {
// happens in substring when a dividing line selected
}
}
public void onEvent(Event ev) {
if (ev instanceof ControlEvent && ev.type == ControlEvent.PRESSED) {
if (ev.target == executePanel.cancelButton) {
selectedMap = null;
selected = false;
this.close(FormBase.IDCANCEL);
}
if (ev.target == executePanel.applyButton || ev.target == list) {
// ev.target == list is posted by mList if a selection was double clicked
mapSelected();
}
}
super.onEvent(ev);
}
public void penDoubleClicked(Point where) {
mapSelected();
}
}
class ArrowsOnMap extends AniImage {
float gotoDir = -361;
float sunDir = -361;
float moveDir = -361;
int minY;
Graphics draw;
private MapInfoObject map = null;
Color moveDirColor = new Color(255, 0, 0); // RED
final static Color sunDirColor = new Color(255, 255, 0); // Yellow
// final static Color GREEN = new Color(0,255,0);
final static Color gotoDirColor = new Color(0, 0, 128); // dark blue
final static Color northDirColor = new Color(0, 0, 255); // Blue
Point[] sunDirArrow = null;
Point[] gotoDirArrow = null;
Point[] moveDirArrow = null;
Point[] northDirArrow = null;
int imageSize = Preferences.itself().fontSize * 8;
int arrowThickness = imageSize / 28;
/**
* @param gd
* goto direction
* @param sd
* sun direction
* @param md
* moving direction
*/
public ArrowsOnMap() {
super();
newImage();
// setDirections(90, 180, -90);
}
public void newImage() {
setImage(new Image(imageSize, imageSize), Color.White);
draw = new Graphics(image);
}
public void setMap(MapInfoObject m) {
map = m;
makeArrows();
}
public void setDirections(float gd, float sd, float md) {
// to save cpu-usage only update if the is a change of directions of more than 1 degree
if (java.lang.Math.abs(gotoDir - gd) > 1 || java.lang.Math.abs(sunDir - sd) > 1 || java.lang.Math.abs(moveDir - md) > 1) {
gotoDir = gd;
sunDir = sd;
moveDir = md;
makeArrows();
}
}
/**
* draw arrows for the directions of movement and destination waypoint
*
* @param ctrl
* the control to paint on
* @param moveDir
* degrees of movement
* @param destDir
* degrees of destination waypoint
*/
public void doDraw(Graphics g, int options) {
if (map == null || g == null)
return;
drawArrows(g);
return;
/*
if (!dirsChanged) {
g.drawImage(image,mask,transparentColor,0,-minY,location.width,location.height);
// the transparency with a transparent color doesn't work in ewe-vm for pocketpc, it works in java-vm, ewe-vm on pocketpc2003
return;
}
dirsChanged = false;
//super.doDraw(g, options);
draw.setColor(Color.White);
draw.fillRect(0, 0, location.width, location.height);
minY = Integer.MAX_VALUE;
drawArrows(draw);
draw.drawImage(image,mask,Color.DarkBlue,0,0,location.width,location.height);
// this trick (note: wrong transparentColor) forces a redraw
g.drawImage(image,mask,transparentColor,0,-minY,location.width,location.height);
*/
}
private void makeArrows() {
// draw only valid arrows
if (moveDir < 360 && moveDir > -360) {
if (moveDirArrow == null)
moveDirArrow = new Point[2];
makeArrow(moveDirArrow, moveDir, 1.0f);
} else
moveDirArrow = null;
if (gotoDir < 360 && gotoDir > -360) {
if (gotoDirArrow == null)
gotoDirArrow = new Point[2];
makeArrow(gotoDirArrow, gotoDir, 1.0f);
} else
gotoDirArrow = null;
if (sunDir < 360 && sunDir > -360) {
if (sunDirArrow == null)
sunDirArrow = new Point[2];
makeArrow(sunDirArrow, sunDir, 0.75f);
} else
sunDirArrow = null;
// show northth arrow only if it has more than 1.5 degree deviation from vertical direction
if (map != null && java.lang.Math.abs(map.rotationRad) > 1.5 / 180 * java.lang.Math.PI) {
if (northDirArrow == null)
northDirArrow = new Point[2];
makeArrow(northDirArrow, 0, 1.0f); // north direction
} else
northDirArrow = null;
// select moveDirColor according to difference to gotoDir
moveDirColor = new Color(255, 0, 0); // red
if (moveDirArrow != null && gotoDirArrow != null) {
float diff = java.lang.Math.abs(moveDir - gotoDir);
while (diff > 360) {
diff -= 360.0f;
}
if (diff > 180) {
diff = 360.0f - diff;
}
if (diff <= 5.0) {
moveDirColor = new Color(0, 192, 0);// darkgreen
} else if (diff <= 22.5) {
moveDirColor = new Color(0, 255, 0);// green
} else if (diff <= 45.0) {
moveDirColor = new Color(255, 128, 0);// orange
}
}
}
/**
* make (calculate) Pixel array for a single arrow
*
* @param g
* handle for drawing
* @param angle
* angle of arrow
* @param col
* color of arrow
*/
private void makeArrow(Point[] arrow, float angle, float scale) {
if (map == null)
return;
float angleRad;
final int centerX = location.width / 2, centerY = location.height / 2;
if (arrow[0] == null)
arrow[0] = new Point();
if (arrow[1] == null)
arrow[1] = new Point();
arrow[0].x = centerX;
arrow[0].y = centerY;
angleRad = angle * (float) java.lang.Math.PI / 180 + map.rotationRad;
arrow[1].x = centerX + new Float(centerX * java.lang.Math.sin(angleRad) * scale).intValue();
arrow[1].y = centerY - new Float(centerY * java.lang.Math.cos(angleRad) * scale).intValue();
// g.setPen(new Pen(Color.Black,Pen.SOLID,7));
// g.drawLine(centerX,centerY,x,y);
}
public void drawArrows(Graphics g) {
drawArrow(g, northDirArrow, northDirColor);
drawArrow(g, gotoDirArrow, gotoDirColor);
drawArrow(g, moveDirArrow, moveDirColor);
drawArrow(g, sunDirArrow, sunDirColor);
}
public void drawArrow(Graphics g, Point[] arrow, Color col) {
if (arrow == null)
return;
g.setPen(new Pen(col, Pen.SOLID, arrowThickness));
g.drawLine(arrow[0].x, arrow[0].y, arrow[1].x, arrow[1].y);
}
}