[med-svn] [imagej] 02/07: Imported Upstream version 150i+dfsg

Andreas Tille tille at debian.org
Thu Apr 7 09:13:48 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository imagej.

commit 192510f7a9c3da2f2d951d0c519b1ac06a0a721f
Author: Andreas Tille <tille at debian.org>
Date:   Thu Apr 7 10:53:25 2016 +0200

    Imported Upstream version 150i+dfsg
---
 IJ_Props.txt                                   |   7 +-
 ij/CompositeImage.java                         |  26 ++
 ij/Executer.java                               |  14 +-
 ij/IJ.java                                     |  58 +++-
 ij/ImageJ.java                                 |   8 +-
 ij/ImagePlus.java                              |  46 ++-
 ij/ImageStack.java                             |   6 +-
 ij/Menus.java                                  |  59 ++--
 ij/OtherInstance.java                          |  13 +-
 ij/Prefs.java                                  |   7 +-
 ij/Undo.java                                   |   6 +-
 ij/WindowManager.java                          |  20 +-
 ij/gui/GenericDialog.java                      |   5 +-
 ij/gui/HistogramWindow.java                    |   6 +-
 ij/gui/ImageCanvas.java                        |  35 +--
 ij/gui/ImageRoi.java                           |   5 +-
 ij/gui/ImageWindow.java                        |  36 ++-
 ij/gui/Plot.java                               | 218 ++++++++++----
 ij/gui/PlotCanvas.java                         |   2 +-
 ij/gui/PlotDialog.java                         |  55 ++--
 ij/gui/PlotWindow.java                         | 185 +++++++-----
 ij/gui/PointRoi.java                           | 245 ++++++++++++---
 ij/gui/PolygonRoi.java                         |   2 +
 ij/gui/Roi.java                                |  53 +++-
 ij/gui/ShapeRoi.java                           |   2 +
 ij/gui/StackWindow.java                        |  16 +-
 ij/io/DirectoryChooser.java                    |   2 +-
 ij/io/FileSaver.java                           |  12 +-
 ij/io/ImageReader.java                         |   2 +
 ij/io/Opener.java                              |  14 +-
 ij/io/PluginClassLoader.java                   |   4 +-
 ij/io/RoiEncoder.java                          |   3 +-
 ij/macro/Functions.java                        |  89 +++++-
 ij/macro/Interpreter.java                      |  21 ++
 ij/measure/Calibration.java                    |   8 +-
 ij/measure/ResultsTable.java                   |   9 +-
 ij/plugin/AVI_Reader.java                      | 112 ++++---
 ij/plugin/Animator.java                        |  14 +-
 ij/plugin/ChannelSplitter.java                 |   3 +
 ij/plugin/CircularRoiMaker.java                |  83 ++++++
 ij/plugin/Clipboard.java                       |  17 ++
 ij/plugin/CommandFinder.java                   |  91 +++++-
 ij/plugin/Compiler.java                        |  99 ++-----
 ij/plugin/Converter.java                       |   9 +-
 ij/plugin/DragAndDrop.java                     |  19 +-
 ij/plugin/Grid.java                            | 263 +++++++++++++++++
 ij/plugin/GroupedZProjector.java               |   2 +
 ij/plugin/{filter/Info.java => ImageInfo.java} |  66 +++--
 ij/plugin/ImageJ_Updater.java                  |  99 ++-----
 ij/plugin/ImagesToStack.java                   |  17 +-
 ij/plugin/JavaScriptEvaluator.java             |  24 +-
 ij/plugin/Macro_Runner.java                    |  20 +-
 ij/plugin/MontageMaker.java                    |   3 +
 ij/plugin/Orthogonal_Views.java                | 151 +++++++---
 ij/plugin/PluginInstaller.java                 |   4 +-
 ij/plugin/PointToolOptions.java                |  75 ++---
 ij/plugin/Profiler.java                        |  29 +-
 ij/plugin/RGBStackMerge.java                   |   5 +-
 ij/plugin/Resizer.java                         |  27 +-
 ij/plugin/ScaleBar.java                        |   4 +-
 ij/plugin/Scaler.java                          |  46 ++-
 ij/plugin/SimpleCommands.java                  |   4 +-
 ij/plugin/StackEditor.java                     |  14 +
 ij/plugin/WindowOrganizer.java                 |   8 +
 ij/plugin/filter/AVI_Writer.java               |  70 +++--
 ij/plugin/filter/Analyzer.java                 |  44 ++-
 ij/plugin/filter/GaussianBlur.java             |  11 +
 ij/plugin/filter/ImageMath.java                |   6 +-
 ij/plugin/filter/Info.java                     | 393 +------------------------
 ij/plugin/filter/MaximumFinder.java            |   2 +
 ij/plugin/filter/ParticleAnalyzer.java         |  10 +-
 ij/plugin/filter/PlugInFilterRunner.java       |   6 +-
 ij/plugin/frame/ContrastAdjuster.java          |  45 ++-
 ij/plugin/frame/Editor.java                    |  45 +--
 ij/plugin/frame/PlugInDialog.java              |  12 +-
 ij/plugin/frame/PlugInFrame.java               |   7 +-
 ij/plugin/frame/Recorder.java                  |  23 +-
 ij/plugin/frame/RoiManager.java                | 268 ++++++++++++-----
 ij/plugin/frame/ThresholdAdjuster.java         |  36 ++-
 ij/plugin/tool/PixelInspectionTool.java        |   2 +
 ij/plugin/tool/RoiRotationTool.java            |  31 +-
 ij/process/AutoThresholder.java                |   8 +-
 ij/process/ByteProcessor.java                  |  11 +-
 ij/process/ColorProcessor.java                 |   9 +-
 ij/process/FloatProcessor.java                 |   4 +-
 ij/process/ImageProcessor.java                 |  45 +--
 ij/process/ShortProcessor.java                 |   4 +-
 ij/process/StackProcessor.java                 |   1 +
 release-notes.html                             |  59 ++--
 89 files changed, 2397 insertions(+), 1362 deletions(-)

diff --git a/IJ_Props.txt b/IJ_Props.txt
index 84b3d55..2727193 100644
--- a/IJ_Props.txt
+++ b/IJ_Props.txt
@@ -97,7 +97,7 @@ selection24="Add to Manager[t]",ij.plugin.Selection("add")
 options01="Line Width...",ij.plugin.Options("line")
 options02="Input/Output...",ij.plugin.Options("io")
 options03="Fonts...",ij.plugin.SimpleCommands("fonts")
-options04="Profile Plot Options...",ij.plugin.Profiler("set")
+options04="Plots...",ij.plugin.Profiler("set")
 options05="Rounded Rect Tool...",ij.plugin.RectToolOptions
 options06="Arrow Tool...",ij.plugin.ArrowToolOptions
 options07="Point Tool...",ij.plugin.PointToolOptions
@@ -304,6 +304,7 @@ fft06="Bandpass Filter...",ij.plugin.filter.FFTFilter
 fft07="Custom Filter...",ij.plugin.filter.FFTCustomFilter
 fft08="FD Math...",ij.plugin.FFTMath
 fft09="Swap Quadrants",ij.plugin.FFT("swap")
+fft10="Make Circular Selection...",ij.plugin.CircularRoiMaker
 
 # Plugins installed in the Process/Filters submenu
 filters01="Convolve...",ij.plugin.filter.Convolver
@@ -349,6 +350,7 @@ tools05="ROI Manager...",ij.plugin.frame.RoiManager
 tools06="Scale Bar...",ij.plugin.ScaleBar
 tools07="Calibration Bar...",ij.plugin.CalibrationBar
 tools08="Synchronize Windows",ij.plugin.frame.SyncWindows
+tools09="Grid...",ij.plugin.Grid
 
 # Plugins installed in the Plugins menu
 plug-in01=>"Macros"
@@ -417,7 +419,8 @@ about02=-
 # URL of directory containing the sample images
 # Used when ImageJ is running as an application,
 # otherwise applet.getDocumentBase()+"/images" is used.
-images.location=http://imagej.nih.gov/ij/images/
+#images.location=http://imagej.nih.gov/ij/images/
+images.location=http://wsr.imagej.net/images/
 
 # Images installed in the Open Samples submenu
 # RawReader expects a string with "name width height nImages bitsPerPixel offset [white]"
diff --git a/ij/CompositeImage.java b/ij/CompositeImage.java
index 067cdd7..985985d 100644
--- a/ij/CompositeImage.java
+++ b/ij/CompositeImage.java
@@ -620,6 +620,32 @@ public class CompositeImage extends ImagePlus {
 		return customLuts && mode!=GRAYSCALE;
 	}
 	
+	public void close() {
+		super.close();
+		rgbPixels = null;
+		imageSource = null;
+		awtImage = null;
+		rgbRaster = null;
+		rgbSampleModel = null;
+		rgbImage = null;
+		rgbCM = null;
+		if (cip!=null) {
+			for (int i=0; i<cip.length; i++)
+				cip[i] = null;
+			cip = null;
+		}
+		if (lut!=null) {
+			for (int i=0; i<lut.length; i++)
+				lut[i] = null;
+			lut = null;
+		}
+		if (channelLuts!=null) {
+			for (int i=0; i<channelLuts.length; i++)
+				channelLuts[i] = null;
+			channelLuts = null;
+		}
+	}
+
 	/** Deprecated */
 	public synchronized void setChannelsUpdated() {
 		if (cip!=null) {
diff --git a/ij/Executer.java b/ij/Executer.java
index 66dff0f..4211e2c 100644
--- a/ij/Executer.java
+++ b/ij/Executer.java
@@ -48,7 +48,8 @@ public class Executer implements Runnable {
 	}
 
 	public void run() {
-		if (command==null) return;
+		if (command==null)
+			return;
 		if (listeners.size()>0) synchronized (listeners) {
 			for (int i=0; i<listeners.size(); i++) {
 				CommandListener listener = (CommandListener)listeners.elementAt(i);
@@ -100,6 +101,10 @@ public class Executer implements Runnable {
 						s = e + "\n \nThis plugin requires Java 1.7 or later.";
 						w=700; h=150;
 					}
+					if (s.indexOf("version 52.0")!=-1) {
+						s = e + "\n \nThis plugin requires Java 1.8 or later.";
+						w=700; h=150;
+					}
 				}
 				if (IJ.getInstance()!=null) {
 					s = IJ.getInstance().getInfo()+"\n \n"+s;
@@ -143,7 +148,12 @@ public class Executer implements Runnable {
 			// is it an example in Help>Examples menu?
 			if (openExample(cmd))
 				return;
-			IJ.error("Unrecognized command: \"" + cmd+"\"");
+			if ("Auto Threshold".equals(cmd)&&(String)table.get("Auto Threshold...")!=null)
+				runCommand("Auto Threshold...");
+			else if ("Enhance Local Contrast (CLAHE)".equals(cmd)&&(String)table.get("CLAHE ")!=null)
+				runCommand("CLAHE ");
+			else
+				IJ.error("Unrecognized command: \"" + cmd+"\"");
 	 	}
     }
     
diff --git a/ij/IJ.java b/ij/IJ.java
index 964c8fb..820860c 100644
--- a/ij/IJ.java
+++ b/ij/IJ.java
@@ -20,6 +20,9 @@ import java.applet.Applet;
 import java.io.*;
 import java.lang.reflect.*;
 import java.net.*;
+import javax.net.ssl.*;
+import java.security.cert.*;
+import java.security.KeyStore;
 
 
 /** This class consists of static utility methods. */
@@ -63,6 +66,7 @@ public class IJ {
 	private static Properties properties;	private static DecimalFormat[] df;
 	private static DecimalFormat[] sf;
 	private static DecimalFormatSymbols dfs;
+	private static boolean trustManagerCreated;
 			
 	static {
 		osname = System.getProperty("os.name");
@@ -327,6 +331,7 @@ public class IJ {
 			commandTable.put("XY Coodinates... ", "XY Coordinates... ");
 			commandTable.put("Statistics...", "Statistics");
 			commandTable.put("Channels Tool... ", "Channels Tool...");
+			commandTable.put("Profile Plot Options...", "Plots...");
 		}
 		String command2 = (String)commandTable.get(command);
 		if (command2!=null)
@@ -604,7 +609,7 @@ public class IJ {
 		Writes to the Java console if ImageJ is not present. */
 	public static void showMessage(String title, String msg) {
 		if (ij!=null) {
-			if (msg!=null && msg.startsWith("<html>")) {
+			if (msg!=null && (msg.startsWith("<html>")||msg.startsWith("<HTML>"))) {
 				HTMLDialog hd = new HTMLDialog(title, msg);
 				if (isMacro() && hd.escapePressed())
 					throw new RuntimeException(Macro.MACRO_CANCELED);
@@ -1670,14 +1675,18 @@ public class IJ {
 		Returns "<Error: message>" if there an error, including
 		host or file not found. */
 	public static String openUrlAsString(String url) {
+		//if (!trustManagerCreated && url.contains("nih.gov")) trustAllCerts();
+		url = Opener.updateUrl(url);
+		if (debugMode) log("OpenUrlAsString: "+url);
 		StringBuffer sb = null;
 		url = url.replaceAll(" ", "%20");
 		try {
+			//if (url.contains("nih.gov")) addRootCA();
 			URL u = new URL(url);
 			URLConnection uc = u.openConnection();
 			long len = uc.getContentLength();
-			if (len>1048576L)
-				return "<Error: file is larger than 1MB>";
+			if (len>5242880L)
+				return "<Error: file is larger than 5MB>";
 			InputStream in = u.openStream();
 			BufferedReader br = new BufferedReader(new InputStreamReader(in));
 			sb = new StringBuffer() ;
@@ -1693,6 +1702,49 @@ public class IJ {
 		else
 			return "";
 	}
+	
+	/* 
+	public static void addRootCA() throws Exception {
+		String path = "/Users/wayne/Downloads/Certificates/lets-encrypt-x1-cross-signed.pem";
+		InputStream fis = new BufferedInputStream(new FileInputStream(path));
+		Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(fis);
+		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+		ks.load(null, null);
+		ks.setCertificateEntry(Integer.toString(1), ca);
+		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+		tmf.init(ks);
+		SSLContext ctx = SSLContext.getInstance("TLS");
+		ctx.init(null, tmf.getTrustManagers(), null); 
+		HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
+	}
+	*/
+	
+	/*
+	// Create a new trust manager that trust all certificates
+	// http://stackoverflow.com/questions/10135074/download-file-from-https-server-using-java
+	private static void trustAllCerts() {
+		trustManagerCreated = true;
+		TrustManager[] trustAllCerts = new TrustManager[] {
+			new X509TrustManager() {
+				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+					return null;
+				}
+				public void checkClientTrusted (java.security.cert.X509Certificate[] certs, String authType) {
+				}
+				public void checkServerTrusted (java.security.cert.X509Certificate[] certs, String authType) {
+				}
+			}
+		};
+		// Activate the new trust manager
+		try {
+			SSLContext sc = SSLContext.getInstance("SSL");
+			sc.init(null, trustAllCerts, new java.security.SecureRandom());
+			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+		} catch (Exception e) {
+			IJ.log(""+e);
+		}
+	}
+	*/
 
 	/** Saves the current image, lookup table, selection or text window to the specified file path. 
 		The path must end in ".tif", ".jpg", ".gif", ".zip", ".raw", ".avi", ".bmp", ".fits", ".pgm", ".png", ".lut", ".roi" or ".txt".  */
diff --git a/ij/ImageJ.java b/ij/ImageJ.java
index eb8df98..202f0dc 100644
--- a/ij/ImageJ.java
+++ b/ij/ImageJ.java
@@ -79,7 +79,7 @@ public class ImageJ extends Frame implements ActionListener,
 	MouseListener, KeyListener, WindowListener, ItemListener, Runnable {
 
 	/** Plugins should call IJ.getVersion() or IJ.getFullVersion() to get the version string. */
-	public static final String VERSION = "1.50d";
+	public static final String VERSION = "1.50i";
 	public static final String BUILD = "";
 	public static Color backgroundColor = new Color(237,237,237);
 	/** SansSerif, 12-point, plain font. */
@@ -606,7 +606,11 @@ public class ImageJ extends Frame implements ActionListener,
 	public void windowActivated(WindowEvent e) {
 		if (IJ.isMacintosh() && !quitting) {
 			IJ.wait(10); // may be needed for Java 1.4 on OS X
-			setMenuBar(Menus.getMenuBar());
+			MenuBar mb = Menus.getMenuBar();
+			if (mb!=null && mb!=getMenuBar()) {
+				setMenuBar(mb);
+				Menus.setMenuBarCount++;
+			}
 		}
 	}
 	
diff --git a/ij/ImagePlus.java b/ij/ImagePlus.java
index 419fdda..c6717ca 100644
--- a/ij/ImagePlus.java
+++ b/ij/ImagePlus.java
@@ -90,6 +90,8 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 	private static int default16bitDisplayRange;
 	private boolean antialiasRendering = true;
 	private boolean ignoreGlobalCalibration;
+	public boolean setIJMenuBar = true;
+	public boolean typeSet;
 	
 
     /** Constructs an uninitialized ImagePlus. */
@@ -156,6 +158,8 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		if (locked) {
 			IJ.beep();
 			IJ.showStatus("\"" + title + "\" is locked");
+			if (IJ.macroRunning())
+				IJ.wait(500);
 			return false;
         } else {
         	locked = true;
@@ -917,7 +921,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 			return;
     	if (win!=null) {
     		if (ij!=null)
-				Menus.updateWindowMenuItem(this.title, title);
+				Menus.updateWindowMenuItem(this, this.title, title);
 			String virtual = stack!=null && stack.isVirtual()?" (V)":"";
 			String global = getGlobalCalibration()!=null?" (G)":"";
 			String scale = "";
@@ -1080,8 +1084,11 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
     	return imageType;
     }
 
-    /** Returns the bit depth, 8, 16, 24 (RGB) or 32. RGB images actually use 32 bits per pixel. */
+    /** Returns the bit depth, 8, 16, 24 (RGB) or 32, or 0 if the bit depth 
+    	is unknown. RGB images actually use 32 bits per pixel. */
     public int getBitDepth() {
+    	if (imageType==GRAY8 && ip==null && img==null && !typeSet)
+    		return 0;
     	int bitDepth = 0;
     	switch (imageType) {
 	    	case GRAY8: case COLOR_256: bitDepth=8; break;
@@ -1101,17 +1108,18 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
     	}
 	}
 
-    protected void setType(int type) {
-    	if ((type<0) || (type>COLOR_RGB))
-    		return;
-    	int previousType = imageType;
-    	imageType = type;
+	protected void setType(int type) {
+		if ((type<0) || (type>COLOR_RGB))
+			return;
+		int previousType = imageType;
+		imageType = type;
+		typeSet = true;
 		if (imageType!=previousType) {
 			if (win!=null)
 				Menus.updateMenus();
 			getLocalCalibration().setImage(this);
 		}
-    }
+	}
 		
  	/** Returns the string value from the "Info" property string  
 	 * associated with 'key', or null if the key is not found. 
@@ -1373,8 +1381,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		}
 	}
 
-	/** Returns the current stack index (one-based) or 1 if
-		this is a single image. */
+	/** Returns the current stack index (one-based) or 1 if this is a single image. */
 	public int getCurrentSlice() {
 		if (currentSlice<1) setCurrentSlice(1);
 		if (currentSlice>getStackSize())
@@ -1621,7 +1628,8 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 				ip.resetRoi();
 		}
 		roi.setImage(this);
-		if (updateDisplay) draw();
+		if (updateDisplay)
+			draw();
 		//roi.notifyListeners(RoiListener.CREATED);
 	}
 	
@@ -1728,7 +1736,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		deleteRoi();
 	}
 
-	public void saveRoi() {
+	public synchronized void saveRoi() {
 		if (roi!=null) {
 			roi.endPaste();
 			Rectangle r = roi.getBounds();
@@ -2081,7 +2089,6 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 
    /** Sets this image's calibration. */
     public void setCalibration(Calibration cal) {
-		//IJ.write("setCalibration: "+cal);
 		if (cal==null)
 			calibration = null;
 		else {
@@ -2104,6 +2111,11 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 			return globalCalibration;
     }
 
+    /** This is a version of getGlobalCalibration() that can be called from a static context. */
+    public static Calibration getStaticGlobalCalibration() {
+			return globalCalibration;
+    }
+
 	/** Returns this image's local calibration, ignoring 
 		the "Global" calibration flag. */
 	public Calibration getLocalCalibration() {
@@ -2662,4 +2674,12 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
     	return "img["+getTitle()+" ("+width+"x"+height+"x"+getNChannels()+"x"+getNSlices()+"x"+getNFrames()+")]";
     }
     
+    public void setIJMenuBar(boolean b) {
+    	setIJMenuBar = b;
+    }
+    
+    public boolean setIJMenuBar() {
+    	return setIJMenuBar;
+    }
+    
 }
diff --git a/ij/ImageStack.java b/ij/ImageStack.java
index c15d38d..7bf59e7 100644
--- a/ij/ImageStack.java
+++ b/ij/ImageStack.java
@@ -344,7 +344,11 @@ public class ImageStack {
 		return ("stack["+getWidth()+"x"+getHeight()+"x"+getSize()+v+"]");
 	}
 	
-	/** Returns, as a double, the specified voxel. */
+	/** Returns, as a double, the specified voxel. Use the
+	 * ImagePlus.getStackIndex() method to convert a C,Z,T
+	 * hyperstack position (one-based) into a z index (zero-based).
+	 * @see ij.ImagePlus#getStackIndex
+	*/
 	public final double getVoxel(int x, int y, int z) {
 		if (x>=0 && x<width && y>=0 && y<height && z>=0 && z<nSlices) {
 			switch (type) {
diff --git a/ij/Menus.java b/ij/Menus.java
index 3901ad1..9148eab 100644
--- a/ij/Menus.java
+++ b/ij/Menus.java
@@ -4,6 +4,7 @@ import ij.util.*;
 import ij.gui.ImageWindow;
 import ij.plugin.MacroInstaller;
 import ij.gui.Toolbar;
+import ij.macro.Interpreter;
 import java.awt.*;
 import java.awt.image.*;
 import java.awt.event.*;
@@ -81,6 +82,7 @@ public class Menus {
 	private static Font menuFont;
 
 	static boolean jnlp; // true when using Java WebStart
+	public static int setMenuBarCount;
 		
 	Menus(ImageJ ijInstance, Applet appletInstance) {
 		ij = ijInstance;
@@ -152,7 +154,7 @@ public class Menus {
 			
 		image.addSeparator();
 		getMenu("Image>Adjust", true);
-		addPlugInItem(image, "Show Info...", "ij.plugin.filter.Info", KeyEvent.VK_I, false);
+		addPlugInItem(image, "Show Info...", "ij.plugin.ImageInfo", KeyEvent.VK_I, false);
 		addPlugInItem(image, "Properties...", "ij.plugin.filter.ImageProperties", KeyEvent.VK_P, true);
 		getMenu("Image>Color", true);
 		getMenu("Image>Stacks", true);
@@ -248,8 +250,10 @@ public class Menus {
 
 		if (fontSize!=0)
 			mbar.setFont(getFont());
-		if (ij!=null)
+		if (ij!=null) {
 			ij.setMenuBar(mbar);
+			Menus.setMenuBarCount++;
+		}
 		
 		if (pluginError!=null)
 			error = error!=null?error+="\n"+pluginError:pluginError;
@@ -267,6 +271,7 @@ public class Menus {
 		addExample(submenu, "Semi-log Plot", "Semi-log_Plot.ijm");
 		addExample(submenu, "Arrow Plot", "Arrow_Plot.ijm");
 		addExample(submenu, "Process Folder", "Batch_Process_Folder.ijm");
+		addExample(submenu, "OpenDialog Demo", "OpenDialog_Demo.ijm");
 		addExample(submenu, "Sine/Cosine Table", "Sine_Cosine_Table.ijm");
 		addExample(submenu, "Non-numeric Table", "Non-numeric_Table.ijm");
 		addExample(submenu, "Overlay", "Overlay.ijm");
@@ -1292,18 +1297,8 @@ public class Menus {
 	static synchronized void addWindowMenuItem(ImagePlus imp) {
 		if (ij==null) return;
 		String name = imp.getTitle();
-		int size = (imp.getWidth()*imp.getHeight()*imp.getStackSize())/1024;
-		switch (imp.getType()) {
-			case ImagePlus.GRAY32: case ImagePlus.COLOR_RGB: // 32-bit
-				size *=4;
-				break;
-			case ImagePlus.GRAY16:  // 16-bit
-				size *= 2;
-				break;
-			default: // 8-bit
-				;
-		}
-		CheckboxMenuItem item = new CheckboxMenuItem(name + " " + size + "K");
+		String size = ImageWindow.getImageSize(imp);
+		CheckboxMenuItem item = new CheckboxMenuItem(name+" "+size);
 		item.setActionCommand("" + imp.getID());
 		window.add(item);
 		item.addItemListener(ij);
@@ -1312,7 +1307,8 @@ public class Menus {
 	/** Removes the specified item from the Window menu. */
 	static synchronized void removeWindowMenuItem(int index) {
 		//IJ.log("removeWindowMenuItem: "+index+" "+windowMenuItems2+" "+window.getItemCount());
-		if (ij==null) return;
+		if (ij==null)
+			return;
 		try {
 			if (index>=0 && index<window.getItemCount()) {
 				window.remove(WINDOW_MENU_ITEMS+index);
@@ -1329,27 +1325,33 @@ public class Menus {
 
 	/** Changes the name of an item in the Window menu. */
 	public static synchronized void updateWindowMenuItem(String oldLabel, String newLabel) {
-		if (oldLabel==null || oldLabel.equals(newLabel))
+		updateWindowMenuItem(null, oldLabel, newLabel);
+	}
+
+	/** Changes the name of an item in the Window menu. */
+	public static synchronized void updateWindowMenuItem(ImagePlus imp, String oldLabel, String newLabel) {
+		if (oldLabel==null || newLabel==null)
 			return;
 		int first = WINDOW_MENU_ITEMS;
-		int last = window.getItemCount()-1;
-		//IJ.write("updateWindowMenuItem: "+" "+first+" "+last+" "+oldLabel+" "+newLabel);
+		int count = window.getItemCount();
 		try {  // workaround for Linux/Java 5.0/bug
-			for (int i=first; i<=last; i++) {
+			for (int i=first; i<count; i++) {
 				MenuItem item = window.getItem(i);
-				//IJ.write(i+" "+item.getLabel()+" "+newLabel);
 				String label = item.getLabel();
-				if (item!=null && label.startsWith(oldLabel)) {
-					if (label.endsWith("K")) {
-						int index = label.lastIndexOf(' ');
-						if (index>-1)
-							newLabel += label.substring(index, label.length());
-					}
-					item.setLabel(newLabel);
+				if (imp!=null) {  //remove size (e.g. " 24MB")
+					int index = label.lastIndexOf(" ");
+					if (index>-1)
+						label = label.substring(0, index);
+				}
+				if (item!=null && label.equals(oldLabel)) {
+					String size = "";
+					if (imp!=null)
+						size =  " " + ImageWindow.getImageSize(imp);
+					item.setLabel(newLabel+size);
 					return;
 				}
 			}
-		} catch (NullPointerException e) {}
+		} catch (Exception e) {}
 	}
 	
 	/** Adds a file path to the beginning of the File/Open Recent submenu. */
@@ -1612,4 +1614,5 @@ public class Menus {
 		IJ.runPlugIn("ij.plugin.ClassChecker", "");
 		IJ.showStatus("Menus updated: "+m.nPlugins + " commands, " + m.nMacros + " macros");
 	}
+	
 }
diff --git a/ij/OtherInstance.java b/ij/OtherInstance.java
index 32bfc9a..0b2d58d 100644
--- a/ij/OtherInstance.java
+++ b/ij/OtherInstance.java
@@ -35,6 +35,7 @@ import java.util.Properties;
  *	@author Johannes Schindelin
  */
 public class OtherInstance {
+	private static final String DELIMETER = "~!~"; // Separates macro name and argument
 
 	interface ImageJInstance extends Remote {
 		void sendArgument(String arg) throws RemoteException;
@@ -51,12 +52,10 @@ public class OtherInstance {
 				String name = cmd.substring(6);
 				String name2 = name;
 				String arg = null;
-				if (name2.endsWith(")")) {
-					int index = name2.lastIndexOf("(");
-					if (index>0) {
-						name = name2.substring(0, index);
-						arg = name2.substring(index+1, name2.length()-1);
-					}
+				int index = name2.indexOf(DELIMETER);
+				if (index!=-1) {
+					name = name2.substring(0, index);
+					arg = name2.substring(index+DELIMETER.length(), name2.length());
 				}
 				IJ.runMacroFile(name, arg);
 			} else if (cmd.startsWith("run "))
@@ -143,7 +142,7 @@ public class OtherInstance {
 					cmd = "macro " + arg;
 					macros++;
 				} else if (arg.startsWith("-macro") && i+1<args.length) {
-					String macroArg = i+2<args.length?"("+args[i+2]+")":"";
+					String macroArg = i+2<args.length?DELIMETER+args[i+2]:"";
 					cmd = "macro " + args[i+1] + macroArg;
 					instance.sendArgument(cmd);
 					break;
diff --git a/ij/Prefs.java b/ij/Prefs.java
index 04d4668..b905866 100644
--- a/ij/Prefs.java
+++ b/ij/Prefs.java
@@ -54,7 +54,7 @@ public class Prefs {
 
 	private static final int USE_SYSTEM_PROXIES=1<<0, USE_FILE_CHOOSER=1<<1,
 		SUBPIXEL_RESOLUTION=1<<2, ENHANCED_LINE_TOOL=1<<3, SKIP_RAW_DIALOG=1<<4,
-		REVERSE_NEXT_PREVIOUS_ORDER=1<<5, AUTO_RUN_EXAMPLES=1<<6;
+		REVERSE_NEXT_PREVIOUS_ORDER=1<<5, AUTO_RUN_EXAMPLES=1<<6, SHOW_ALL_POINTS=1<<7;
 	public static final String OPTIONS2 = "prefs.options2";
     
 	/** file.separator system property */
@@ -155,6 +155,8 @@ public class Prefs {
 	public static boolean reverseNextPreviousOrder;
 	/** Automatically run examples in Help/Examples menu. */
 	public static boolean autoRunExamples = true;
+	/** Ignore stack positions when displaying points. */
+	public static boolean showAllPoints;
 	
 
 	static Properties ijPrefs = new Properties();
@@ -464,6 +466,7 @@ public class Prefs {
 		skipRawDialog = (options2&SKIP_RAW_DIALOG)!=0;
 		reverseNextPreviousOrder = (options2&REVERSE_NEXT_PREVIOUS_ORDER)!=0;
 		autoRunExamples = (options2&AUTO_RUN_EXAMPLES)!=0;
+		showAllPoints = (options2&SHOW_ALL_POINTS)!=0;
 	}
 
 	static void saveOptions(Properties prefs) {
@@ -489,7 +492,7 @@ public class Prefs {
 			+ (useFileChooser?USE_FILE_CHOOSER:0) + (subPixelResolution?SUBPIXEL_RESOLUTION:0)
 			+ (enhancedLineTool?ENHANCED_LINE_TOOL:0) + (skipRawDialog?SKIP_RAW_DIALOG:0)
 			+ (reverseNextPreviousOrder?REVERSE_NEXT_PREVIOUS_ORDER:0)
-			+ (autoRunExamples?AUTO_RUN_EXAMPLES:0);
+			+ (autoRunExamples?AUTO_RUN_EXAMPLES:0) + (showAllPoints?SHOW_ALL_POINTS:0);
 		prefs.put(OPTIONS2, Integer.toString(options2));
 	}
 
diff --git a/ij/Undo.java b/ij/Undo.java
index 1d63d92..823118b 100644
--- a/ij/Undo.java
+++ b/ij/Undo.java
@@ -37,7 +37,7 @@ public class Undo {
 			reset();
 			return;
 		}
-		//IJ.log(imp.getTitle() + ": set up undo (" + what + ")");
+		if (IJ.debugMode) IJ.log("Undo.setup: "+what+" "+imp);
 		if (what==FILTER && whatToUndo==COMPOUND_FILTER)
 				return;
 		if (what==COMPOUND_FILTER_DONE) {
@@ -91,13 +91,13 @@ public class Undo {
 		calCopy = null;
 		roiCopy = null;
 		lutCopy = null;
-		//IJ.log("Undo: reset");
+		if (IJ.debugMode) IJ.log("Undo.reset");
 	}
 	
 
 	public static void undo() {
 		ImagePlus imp = WindowManager.getCurrentImage();
-		//IJ.log(imp.getTitle() + ": undo (" + whatToUndo + ")  "+(imageID!=imp.getID()));
+		if (IJ.debugMode) IJ.log("Undo.undo: "+ whatToUndo+" "+imp+" "+imageID);
 		if (imp==null || imageID!=imp.getID()) {
 			if (imp!=null && !IJ.macroRunning()) { // does image still have an undo buffer?
 				ImageProcessor ip2 = imp.getProcessor();
diff --git a/ij/WindowManager.java b/ij/WindowManager.java
index 642b696..46ba25a 100644
--- a/ij/WindowManager.java
+++ b/ij/WindowManager.java
@@ -2,10 +2,10 @@ package ij;
 import ij.plugin.Converter;
 import ij.plugin.frame.Recorder;
 import ij.plugin.frame.Editor; 
-import ij.macro.Interpreter;
 import ij.text.TextWindow;
 import ij.plugin.frame.PlugInFrame;
 import ij.util.Tools;
+import ij.macro.Interpreter;
 import java.awt.*;
 import java.util.*;
 import ij.gui.*;
@@ -98,12 +98,26 @@ public class WindowManager {
 			return currentWindow.getImagePlus();
 		else if (frontWindow!=null && (frontWindow instanceof ImageWindow))
 			return frontWindow!=null?((ImageWindow)frontWindow).getImagePlus():null;
-		else 	if (imageList.size()>0) {	
+		else if (imageList.size()>0) {	
+			ImagePlus imp = getFocusManagerActiveImage();
+			if (imp!=null)
+				return imp;
 			ImageWindow win = (ImageWindow)imageList.elementAt(imageList.size()-1);
 			return win.getImagePlus();
 		} else
 			return Interpreter.getLastBatchModeImage(); 
 	}
+	
+	private static ImagePlus getFocusManagerActiveImage() {
+		if (IJ.isMacro())
+			return null;
+		KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
+		Window win = kfm.getActiveWindow();
+		ImagePlus imp = null;
+		if (win!=null && (win instanceof ImageWindow))
+			imp = ((ImageWindow)win).getImagePlus();
+		return imp;
+	}
 
 	/** Returns the number of open image windows. */
 	public static int getWindowCount() {
@@ -570,5 +584,5 @@ public class WindowManager {
 			frame.setState(Frame.NORMAL);
 		frame.toFront();
 	}
-    
+	    
 }
diff --git a/ij/gui/GenericDialog.java b/ij/gui/GenericDialog.java
index 54d61b8..010fd81 100644
--- a/ij/gui/GenericDialog.java
+++ b/ij/gui/GenericDialog.java
@@ -154,7 +154,10 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		}
 		if (IJ.isWindows()) columns -= 2;
 		if (columns<1) columns = 1;
-		TextField tf = new TextField(IJ.d2s(defaultValue, digits), columns);
+		String defaultString = IJ.d2s(defaultValue, digits);
+		if (Double.isNaN(defaultValue))
+			defaultString = "";
+		TextField tf = new TextField(defaultString, columns);
 		if (IJ.isLinux()) tf.setBackground(Color.white);
 		tf.addActionListener(this);
 		tf.addTextListener(this);
diff --git a/ij/gui/HistogramWindow.java b/ij/gui/HistogramWindow.java
index a9347f3..a9be888 100644
--- a/ij/gui/HistogramWindow.java
+++ b/ij/gui/HistogramWindow.java
@@ -427,6 +427,7 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 	public ResultsTable getResultsTable() {
 		ResultsTable rt = new ResultsTable();
 		rt.showRowNumbers(false);
+		rt.setPrecision(digits);
 		String vheading = stats.binSize==1.0?"value":"bin start";
 		if (cal.calibrated() && !cal.isSigned16Bit()) {
 			for (int i=0; i<stats.nBins; i++) {
@@ -434,16 +435,11 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 				rt.setValue(vheading, i, cal.getCValue(stats.histMin+i*stats.binSize));
 				rt.setValue("count", i, histogram[i]);
 			}
-			rt.setDecimalPlaces(0, 0);
-			rt.setDecimalPlaces(1, digits);
-			rt.setDecimalPlaces(2, 0);
 		} else {
 			for (int i=0; i<stats.nBins; i++) {
 				rt.setValue(vheading, i, cal.getCValue(stats.histMin+i*stats.binSize));
 				rt.setValue("count", i, histogram[i]);
 			}
-			rt.setDecimalPlaces(0, digits);
-			rt.setDecimalPlaces(1, 0);
 		}
 		return rt;
 	}
diff --git a/ij/gui/ImageCanvas.java b/ij/gui/ImageCanvas.java
index fdf0bec..dbb14ab 100644
--- a/ij/gui/ImageCanvas.java
+++ b/ij/gui/ImageCanvas.java
@@ -16,7 +16,6 @@ import java.awt.event.*;
 import java.util.*;
 import java.awt.geom.*;
 import java.util.concurrent.atomic.AtomicBoolean;
-//import javax.swing.JPanel;
 
 
 /** This is a Canvas used to display images in a Window. */
@@ -552,18 +551,6 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
         return new Dimension(dstWidth, dstHeight);
     }
 
-    int count;
-    
-    /*
-    public Graphics getGraphics() {
-     	Graphics g = super.getGraphics();
-		IJ.write("getGraphics: "+count++);
-		if (IJ.altKeyDown())
-			throw new IllegalArgumentException("");
-    	return g;
-    }
-    */
-
 	/** Returns the current cursor location in image coordinates. */
 	public Point getCursorLoc() {
 		return new Point(xMouse, yMouse);
@@ -1073,7 +1060,6 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 	}
 
 	public void mousePressed(MouseEvent e) {
-		//if (ij==null) return;
 		showCursorStatus = true;
 		int toolID = Toolbar.getToolId();
 		ImageWindow win = imp.getWindow();
@@ -1088,8 +1074,7 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 		int x = e.getX();
 		int y = e.getY();
 		flags = e.getModifiers();
-		//IJ.log("Mouse pressed: " + e.isPopupTrigger() + "  " + ij.modifiers(flags));		
-		//if (toolID!=Toolbar.MAGNIFIER && e.isPopupTrigger()) {
+		
 		if (toolID!=Toolbar.MAGNIFIER && (e.isPopupTrigger()||(!IJ.isMacintosh()&&(flags&Event.META_MASK)!=0))) {
 			handlePopupMenu(e);
 			return;
@@ -1134,12 +1119,10 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 				if (IJ.shiftKeyDown())
 					zoomToSelection(ox, oy);
 				else if ((flags & (Event.ALT_MASK|Event.META_MASK|Event.CTRL_MASK))!=0) {
-					//IJ.run("Out");
 					zoomOut(x, y);
 					if (getMagnification()<1.0)
 						imp.repaintWindow();
 				} else {
-					//IJ.run("In");
 	 				zoomIn(x, y);
 					if (getMagnification()<=1.0)
 						imp.repaintWindow();
@@ -1290,7 +1273,21 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 		boolean multiPointMode = roi!=null && (roi instanceof PointRoi) && handle==-1
 			&& Toolbar.getToolId()==Toolbar.POINT && Toolbar.getMultiPointMode();
 		if (multiPointMode) {
-			((PointRoi)roi).addPoint(offScreenXD(sx), offScreenYD(sy));
+			double oxd = offScreenXD(sx);
+			double oyd = offScreenYD(sy);
+			if (e.isShiftDown()) {
+				FloatPolygon points = roi.getFloatPolygon();
+				if (points.npoints>0) {
+					double x0 = points.xpoints[0];
+					double y0 = points.ypoints[0];
+					double slope = Math.abs((oxd-x0)/(oyd-y0));
+					if (slope>=1.0)
+						oyd = points.ypoints[0];
+					else
+						oxd = points.xpoints[0];
+				}
+			}
+			((PointRoi)roi).addPoint(imp, oxd, oyd);
 			imp.setRoi(roi);
 			return;
 		}
diff --git a/ij/gui/ImageRoi.java b/ij/gui/ImageRoi.java
index c465147..d8029db 100644
--- a/ij/gui/ImageRoi.java
+++ b/ij/gui/ImageRoi.java
@@ -122,8 +122,9 @@ public class ImageRoi extends Roi {
 	}
 
 	public synchronized Object clone() {
+		ImageRoi roi2 = (ImageRoi)super.clone();
 		ImagePlus imp = new ImagePlus("", img);
-		ImageRoi roi2 = new ImageRoi(x, y, imp.getProcessor());
+		roi2.setProcessor(imp.getProcessor());
 		roi2.setOpacity(getOpacity());
 		roi2.setZeroTransparent(zeroTransparent);
 		return roi2;
@@ -141,6 +142,8 @@ public class ImageRoi extends Roi {
 	public void setProcessor(ImageProcessor ip) {
 		img = ip.createImage();
 		this.ip = ip;
+		width = ip.getWidth();
+		height = ip.getHeight();
 	}
 
 }
\ No newline at end of file
diff --git a/ij/gui/ImageWindow.java b/ij/gui/ImageWindow.java
index d5c2369..9e2cd82 100644
--- a/ij/gui/ImageWindow.java
+++ b/ij/gui/ImageWindow.java
@@ -8,6 +8,7 @@ import ij.process.*;
 import ij.io.*;
 import ij.measure.*;
 import ij.plugin.frame.*;
+import ij.plugin.PointToolOptions;
 import ij.macro.Interpreter;
 import ij.util.*;
 
@@ -546,17 +547,20 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 
 	public void windowActivated(WindowEvent e) {
 		if (IJ.debugMode) IJ.log("windowActivated: "+imp.getTitle());
+		if (IJ.isMacOSX())
+			setImageJMenuBar(this);
+		if (imp==null)
+			return;
 		ImageJ ij = IJ.getInstance();
-		boolean quitting = ij!=null && ij.quitting();
-		if (IJ.isMacintosh() && ij!=null && !quitting) {
-			IJ.wait(10); // may be needed for Java 1.4 on OS X
-			setMenuBar(Menus.getMenuBar());
-		}
-		if (imp==null) return;
-		if (!closed && !quitting && !Interpreter.isBatchMode())
+		if (ij!=null && !closed && !ij.quitting() && !Interpreter.isBatchMode())
 			WindowManager.setCurrentWindow(this);
 		if (imp.isComposite())
 			Channels.updateChannels();
+		Roi roi = imp.getRoi();
+		if (roi!=null && (roi instanceof PointRoi))
+			PointToolOptions.update();
+		if (imp.isComposite())
+			Channels.updateChannels();
 		imp.setActivated(); // notify ImagePlus that image has been activated
 	}
 	
@@ -683,7 +687,23 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 	public int getSliderHeight() {
 		return sliderHeight;
 	}
-
+	
+	public static void setImageJMenuBar(ImageWindow win) {
+		ImageJ ij = IJ.getInstance();
+		boolean setMenuBar = true;
+		ImagePlus imp = win.getImagePlus();
+		if (imp!=null)
+			setMenuBar = imp.setIJMenuBar();
+		MenuBar mb = Menus.getMenuBar();
+		if (mb!=null && mb==win.getMenuBar())
+			setMenuBar = false;
+		if (ij!=null && !ij.quitting() && !Interpreter.nonBatchMacroRunning() && setMenuBar) {
+			IJ.wait(10); // may be needed for Java 1.4 on OS X
+			win.setMenuBar(mb);
+			Menus.setMenuBarCount++;
+		}
+		if (imp!=null) imp.setIJMenuBar(true);
+	}
 			
 } //class ImageWindow
 
diff --git a/ij/gui/Plot.java b/ij/gui/Plot.java
index e2a4f60..7e3355e 100644
--- a/ij/gui/Plot.java
+++ b/ij/gui/Plot.java
@@ -177,6 +177,7 @@ public class Plot implements Cloneable {
 	private int currentJustification = LEFT;
 	private boolean ignoreForce2Grid;				// after explicit setting of range (limits), ignore 'FORCE2GRID' flags
 	//private boolean snapToMinorGrid;				// snap to grid when zooming to selection
+	private Color backgroundColor;
 	
 	/** Construct a new PlotWindow.
 	 *	Note that the data xValues, yValues passed with the constructor are plotted last,
@@ -234,20 +235,79 @@ public class Plot implements Cloneable {
 		return imp == null ? title : imp.getTitle();
 	}
 
-	/** Sets the x-axis and y-axis range. Updates the image if existing. */
-
-	public void setLimits(double xMin, double xMax, double yMin, double yMax) {
-		defaultMinMax[0] = xMin;
-		defaultMinMax[1] = xMax;
-		defaultMinMax[2] = yMin;
-		defaultMinMax[3] = yMax;
-		enlargeRange = null;
-		ignoreForce2Grid = true;
-		if (plotDrawn)
-			setLimitsToDefaults(true);
-	}
-
-	/** Returns the current limits as an array xMin, xMax, yMin, yMax.
+    //n__ begin setLimits
+    /**
+     * Sets the x-axis and y-axis range. Updates the image if existing.
+     * Accepts NaN values to indicate auto-range
+     */
+    public void setLimits(double xMin, double xMax, double yMin, double yMax) {
+        boolean containsNaN = (Double.isNaN(xMin + xMax + yMin + yMax));
+        if (containsNaN && allPlotObjects.isEmpty())//can't apply auto-range without data
+            return;
+        boolean[] auto = new boolean[4];
+        double[] range = {xMin, xMax, yMin, yMax};
+        if (containsNaN) {
+            double[] extrema = getMinAndMax(true, 0xff);
+
+            for (int jj = 0; jj < 4; jj++)
+                if (Double.isNaN(range[jj])) {
+                    range[jj] = extrema[jj];
+                    auto[jj] = true;
+                }
+            double left = range[0];
+            double right = range[1];
+            double bottom = range[2];
+            double top = range[3];
+            
+            //set semi-auto to full-auto if it would result in reverse axis direction
+            if ((auto[0] || auto[1]) && (left >= right)) {
+                left = extrema[0];
+                right = extrema[1];
+                auto[0] = true;
+                auto[1] = true;
+            }
+            if ((auto[2] || auto[3]) && (bottom >= top)) {
+                bottom = extrema[2];
+                top = extrema[3];
+                auto[2] = true;
+                auto[3] = true;
+            }    
+            //Add 3% extra space to automatic borders
+            double extraXLin = (right - left) * 0.03;
+            double extraYLin = (top - bottom) * 0.03;
+            double extraXLog = (Math.log(right) - Math.log(left)) * 0.03;
+            double extraYLog = (Math.log(top) - Math.log(bottom)) * 0.03;
+
+            boolean isLogX = hasFlag(X_LOG_NUMBERS);
+            boolean isLogY = hasFlag(Y_LOG_NUMBERS);
+
+            if (auto[0] && !isLogX)
+                range[0] = left - extraXLin;//extra space (linear)
+            if (auto[1] && !isLogX)
+                range[1] = right + extraXLin;
+            if (auto[2] && !isLogY)
+                range[2] = bottom - extraYLin;
+            if (auto[3] && !isLogY)
+                range[3] = top + extraYLin;
+
+            if (auto[0] && isLogX)
+                range[0] = Math.exp(Math.log(left) - extraXLog);//extra space (log)
+            if (auto[1] && isLogX)
+                range[1] = Math.exp(Math.log(right) + extraXLog);
+            if (auto[2] && isLogY)
+                range[2] = Math.exp(Math.log(bottom) - extraYLog);
+            if (auto[3] && isLogY)
+                range[3] = Math.exp(Math.log(top) + extraYLog);
+        }
+        defaultMinMax = range;//change pointer of defaultMinMax
+        enlargeRange = null;
+        ignoreForce2Grid = true;
+        if (plotDrawn)
+            setLimitsToDefaults(true);
+    }
+    //n__ end setLimits
+   
+    /** Returns the current limits as an array xMin, xMax, yMin, yMax.
 	 *	Note that future versions might return a longer array (e.g. for y2 axis limits) */
 	public double[] getLimits() {
 		return new double[] {xMin, xMax, yMin, yMax};
@@ -256,7 +316,7 @@ public class Plot implements Cloneable {
 	/** Sets the canvas size in unscaled pixels and sets the scale to 1.0.
 	 * If the scale remains 1.0, this will be the size of the resulting ImageProcessor.
 	 * When not called, the canvas size is adjusted for the plot frame size specified
-	 * in Edit>Options>Profile Plot Options. */
+	 * in Edit>Options>Plots. */
 	public void setSize(int width, int height) {
 		//IJ.log("setSize "+width+"x"+height+ " old: "+ip);
 		if (ip != null && width == ip.getWidth() && height == ip.getHeight()) return;
@@ -698,10 +758,22 @@ public class Plot implements Cloneable {
 			isColor = true;
 	}
 	
+	/** Sets the drawing color for the next objects that will be added to the plot. */
 	public void setColor(String c1, String c2) {
 		setColor(Colors.getColor(c1,Color.black), Colors.getColor(c2,Color.black));
 	}
 
+	/** Set the plot frame background color. */
+	public void setBackgroundColor(Color c) {
+		backgroundColor = c;
+		isColor = true;
+	}
+
+	/** Set the plot frame background color. */
+	public void setBackgroundColor(String c) {
+		setBackgroundColor(Colors.getColor(c,Color.white));
+	}
+
 	/** Changes the line width for the next objects that will be added to the plot. */
 	public void setLineWidth(int lineWidth) {
 		currentLineWidth = lineWidth;
@@ -769,7 +841,7 @@ public class Plot implements Cloneable {
 
 	/** Sets the xLabelFont; must not be mull. If this method is not used, the last setFont
 	 *	of setFontSize call before displaying the plot determines the font, or if neither
-	 *	was called, the font size of the Profile Plot Options is used. */
+	 *	was called, the font size of the Plot Options is used. */
 	public void setXLabelFont(Font font) {
 		xLabelFont = font;
 	}
@@ -846,20 +918,18 @@ public class Plot implements Cloneable {
 		if (imp != null) {
 			if (imp.getProcessor() != ip) imp.setProcessor(ip);
 			return imp;
-		}
-		ImagePlus imp = new ImagePlus(title, ip);
-		imp.setIgnoreGlobalCalibration(true);
-		Calibration cal = imp.getCalibration();
-		adjustCalibration(cal);
-		if (this.imp == null)
-			this.imp = imp;
-		imp.setProperty(PROPERTY_KEY, this);
-		return imp;
+		} else {
+		    ImagePlus imp = new ImagePlus(title, ip);
+		    setImagePlus(imp);
+    		return imp;
+    	}
 	}
 
 	/** Sets the ImagePlus where the plot will be displayed. If the ImagePlus is not
 	 *	known otherwise (e.g. from getImagePlus), this is needed for changes such as
-	 *	zooming in to work correctly. It also sets the calibration of the ImagePlus. */
+	 *	zooming in to work correctly. It also sets the calibration of the ImagePlus.
+	 *  The ImagePlus is not displayed or updated.
+	 *  'imp' may be null to disconnect the plot from its ImagePlus */
 	public void setImagePlus(ImagePlus imp) {
 		if (this.imp != null)
 			this.imp.setProperty(PROPERTY_KEY, null);
@@ -889,7 +959,9 @@ public class Plot implements Cloneable {
 			yScale = Double.POSITIVE_INFINITY;
 	}
 
-	/** Displays the plot in a PlotWindow and returns a reference to the PlotWindow. */
+	/** Displays the plot in a PlotWindow and returns a reference to the PlotWindow.
+	 *  Note that the PlotWindow might get closed immediately if its 'listValues' and 'autoClose'
+	 *  flags are set */
 	public PlotWindow show() {
 		if ((IJ.macroRunning() && IJ.getInstance()==null) || Interpreter.isBatchMode()) {
 			imp = getImagePlus();
@@ -906,13 +978,10 @@ public class Plot implements Cloneable {
 			if (win instanceof PlotWindow && win.isVisible()) {
 				updateImage();			// show in existing window
 				return (PlotWindow)win;
-			}
+			} else
+				setImagePlus(null);
 		}
-		PlotWindow pw = new PlotWindow(this);
-		if (imp == null)
-			imp.setProperty(PROPERTY_KEY, null);
-		imp = pw.getImagePlus();
-		imp.setProperty(PROPERTY_KEY, this);
+		PlotWindow pw = new PlotWindow(this);       //note: this may set imp to null if pw has listValues and autoClose are set
 		if (IJ.isMacro() && imp!=null) // wait for plot to be displayed
 			IJ.selectWindow(imp.getID());
 		return pw;
@@ -949,7 +1018,7 @@ public class Plot implements Cloneable {
 	public boolean isFrozen() {
 		return frozen;
 	}
-
+	
 	/** Draws the plot again, ignored if the plot has not been drawn before or the plot is frozen
 	 *	If the ImagePlus exist, updates it and its calibration. */
 	public void updateImage() {
@@ -1200,11 +1269,17 @@ public class Plot implements Cloneable {
 		else
 			Arrays.fill((byte[])(ip.getPixels()), invertedLut ? (byte)0 : (byte)0xff);
 
-		ip.setColor(Color.black);
 		ip.setFont(scFont(defaultFont));
 		ip.setLineWidth(sc(1));
 		ip.setAntialiasedText(antialiasedText);
 		frame = new Rectangle(leftMargin, topMargin, frameWidth+1, frameHeight+1);
+		if (backgroundColor!=null) {
+			ip.setColor(backgroundColor);
+			ip.setRoi(frame);
+			ip.fill();
+			ip.resetRoi();
+		}
+		ip.setColor(Color.black);
 		return ip;
 	}
 
@@ -1440,32 +1515,43 @@ public class Plot implements Cloneable {
 		updateImage();
 	}
 
-	/** Zooms in or out when the user clicks one of the overlay arrows at the axes.
-	 *	Index numbers start with 0 at the 'down' arrow of the lower side of the x axis
-	 *	and end with the up arrow at the upper side of the y axis. */
-	void zoomOnRangeArrow(int arrowIndex) {
-		int axisIndex = (arrowIndex / 4) * 2;  //0 for x, 2 for y
-		double min = axisIndex==0 ? xMin : yMin;
-		double max = axisIndex==0 ? xMax : yMax;
-		double range = max - min;
-		boolean isMin = (arrowIndex % 4) < 2;
-		boolean shrinkRange = arrowIndex % 4 == 1 || arrowIndex % 4 == 2;
-		double factor = Math.sqrt(2);
-		if (shrinkRange) factor = 1.0/factor;
-		if (isMin)
-			min = max - range*factor;
-		else
-			max = min + range*factor;
-		boolean logAxis = axisIndex==0 ? logXAxis : logYAxis;
-		if (logAxis) {
-			min = Math.pow(10, min);
-			max = Math.pow(10, max);
-		}
-		currentMinMax[axisIndex] = min;
-		currentMinMax[axisIndex+1] = max;
-		updateImage();
-	}
-
+    //n__ begin zoomOnRangeArrow
+    /**
+     * Zooms in or out when the user clicks one of the overlay arrows at the
+     * axes. Index numbers start with 0 at the 'down' arrow of the lower side of
+     * the x axis and end with the up arrow at the upper side of the y axis.
+     */
+    void zoomOnRangeArrow(int arrowIndex) {
+        if (arrowIndex < 8) {//0..7 = arrows, 8 = Reset Range
+            int axisIndex = (arrowIndex / 4) * 2;  //0 for x, 2 for y
+            double min = axisIndex == 0 ? xMin : yMin;
+            double max = axisIndex == 0 ? xMax : yMax;
+            double range = max - min;
+            boolean isMin = (arrowIndex % 4) < 2;
+            boolean shrinkRange = arrowIndex % 4 == 1 || arrowIndex % 4 == 2;
+            double factor = Math.sqrt(2);
+            if (shrinkRange)
+                factor = 1.0 / factor;
+            if (isMin)
+                min = max - range * factor;
+            else
+                max = min + range * factor;
+            boolean logAxis = axisIndex == 0 ? logXAxis : logYAxis;
+            if (logAxis) {
+                min = Math.pow(10, min);
+                max = Math.pow(10, max);
+            }
+            currentMinMax[axisIndex] = min;
+            currentMinMax[axisIndex + 1] = max;
+        }
+
+        if (arrowIndex == 8)
+            setLimitsToDefaults(false);
+        updateImage();
+    }
+//n__ end zoomOnRangeArrow
+    
+    
 	/** Zooms in or out on a point x, y in screen coordinates. If x>0, default in both directions,
 	 *	if the cursor is below the x axis, only in x direction, if the cursor is left of the y axis, only in y direction.
 	 *	If x < 0, zooms on center; if x == ZOOM_AS_PREVIOUS, zooms on the center of the previous zoom
@@ -2094,7 +2180,7 @@ public class Plot implements Cloneable {
 			x2 = scaleX(x[i]);
 			y2 = scaleY(y[i]);
 			isNaN2 = Float.isNaN(x[i]) || Float.isNaN(y[i]) || (logXAxis && x[i]<=0) || (logYAxis && y[i]<=0);
-			if (!isNaN1 && !isNaN1)
+			if (!isNaN1 && !isNaN2)
 				ip.drawLine(x1, y1, x2, y2);
 		}
 	}
@@ -2343,6 +2429,11 @@ public class Plot implements Cloneable {
 		return labels;
 	}
 
+	/** Creates a ResultsTable with the plot data. Returns an empty table if no data. */
+	public ResultsTable getResultsTable() {
+		return getResultsTable(true);
+	}
+
 	/** Creates a ResultsTable with the data of the plot. Returns an empty table if no data. 
 	 *	Does not write the first x column if writeFirstXColumn is false.
 	 *	x columns equal to the first x column are never written, independent of writeFirstXColumn */
@@ -2364,12 +2455,11 @@ public class Plot implements Cloneable {
 		int dataSetNumber = 0;
 		int arrowsNumber = 0;
 		PlotObject firstXYobject = null;
-		boolean isFirstXYobject;
 		for (PlotObject plotObject : allPlotObjects) {
 			if (plotObject.type==PlotObject.XY_DATA) {
 				boolean sameX =	 firstXYobject != null && Arrays.equals(firstXYobject.xValues, plotObject.xValues);
 				boolean sameXY = sameX && Arrays.equals(firstXYobject.yValues, plotObject.yValues); //ignore duplicates (e.g. Markers plus Curve)
-				boolean writeX = (firstXYobject==null && writeFirstXColumn) || !sameX;
+				boolean writeX = firstXYobject==null?writeFirstXColumn:!sameX;
 				addToLists(headings, data, plotObject, dataSetNumber, writeX, /*writeY=*/!sameXY, nDataSets>1);
 				if (firstXYobject == null) firstXYobject = plotObject;
 				dataSetNumber++;
diff --git a/ij/gui/PlotCanvas.java b/ij/gui/PlotCanvas.java
index e811bf2..a670448 100644
--- a/ij/gui/PlotCanvas.java
+++ b/ij/gui/PlotCanvas.java
@@ -119,7 +119,7 @@ public class PlotCanvas extends ImageCanvas {
 	}
 
 	/** Implements the Image/Zoom/View 100% command: Sets the original frame size as specified
-	 *	in Profile Plot Options (unless the plot is frozen) */
+	 *	in Edit/Options/Plots (unless the plot is frozen) */
 	public void zoom100Percent() {
 		if (plot == null || plot.isFrozen()) {
 			super.zoom100Percent();
diff --git a/ij/gui/PlotDialog.java b/ij/gui/PlotDialog.java
index df6380d..ba4b9cd 100644
--- a/ij/gui/PlotDialog.java
+++ b/ij/gui/PlotDialog.java
@@ -50,61 +50,64 @@ public class PlotDialog {
 			boolean livePlot = plot.plotMaker != null;
 			int xDigits = plot.logXAxis ? -2 : Plot.getDigits(currentMinMax[0], currentMinMax[1], 0.005*Math.abs(currentMinMax[1]-currentMinMax[0]), 6);
 			int yDigits = plot.logYAxis ? -2 : Plot.getDigits(currentMinMax[2], currentMinMax[3], 0.005*Math.abs(currentMinMax[3]-currentMinMax[2]), 6);
-			gd.addNumericField("X_From", currentMinMax[0], xDigits);
-			gd.addNumericField("X_To", currentMinMax[1], xDigits);
+			gd.addNumericField("X_From*", currentMinMax[0], xDigits);
+			gd.addNumericField("X_To*", currentMinMax[1], xDigits);
 			gd.setInsets(0, 20, 0); //top, left, bottom
-			if (livePlot) gd.addCheckbox("Fix_X Range While Live", (plot.templateFlags&Plot.X_RANGE)!=0);
+			if (livePlot)
+				gd.addCheckbox("Fix_X Range While Live", (plot.templateFlags & Plot.X_RANGE) != 0);
 			gd.addCheckbox("Log_X Axis", (plot.hasFlag(Plot.X_LOG_NUMBERS)));
 			gd.setInsets(20, 0, 3); //top, left, bottom
-			gd.addNumericField("Y_From", currentMinMax[2], yDigits);
-			gd.addNumericField("Y_To", currentMinMax[3], yDigits);
-			if (livePlot) gd.addCheckbox("Fix_Y Range While Live", (plot.templateFlags&Plot.Y_RANGE)!=0);
+			gd.addNumericField("Y_From*", currentMinMax[2], yDigits);
+			gd.addNumericField("Y_To*", currentMinMax[3], yDigits);
+			if (livePlot)
+				gd.addCheckbox("Fix_Y Range While Live", (plot.templateFlags & Plot.Y_RANGE) != 0);
 			gd.addCheckbox("Log_Y Axis", (plot.hasFlag(Plot.Y_LOG_NUMBERS)));
+			gd.addMessage("*Leave empty for automatic range", new Font("SansSerif", Font.PLAIN, 12));        //n__ begin PlotDialog
 			gd.showDialog();
-			if (gd.wasCanceled()) return;
-
+			if (gd.wasCanceled())
+				return;
 			plot.saveMinMax();
-			String errorWhat = "";
 			double linXMin = gd.getNextNumber();
+			if (gd.invalidNumber())
+				linXMin = Double.NaN;
 			double linXMax = gd.getNextNumber();
 			if (gd.invalidNumber())
-				errorWhat = "X";
-			else {
-				currentMinMax[0] = linXMin;
-				currentMinMax[1] = linXMax;
-			}
-
+				linXMax = Double.NaN;
 			double linYMin = gd.getNextNumber();
+			if (gd.invalidNumber())
+				linYMin = Double.NaN;
 			double linYMax = gd.getNextNumber();
 			if (gd.invalidNumber())
-				errorWhat +=" Y";
-			else {
-				currentMinMax[2] = linYMin;
-				currentMinMax[3] = linYMax;
-			}
-			if (errorWhat.length()>0) {
-				IJ.error("Invalid Input", errorWhat+" Range remains unchanged");
-				return;
-			}
+				linYMax = Double.NaN;
+
+			currentMinMax[0] = linXMin;
+			currentMinMax[1] = linXMax;
+			currentMinMax[2] = linYMin;
+			currentMinMax[3] = linYMax;
+			
 			if (livePlot) plot.templateFlags = setFlag(plot.templateFlags, Plot.X_RANGE, gd.getNextBoolean());
 			boolean xLog = gd.getNextBoolean();
 			if (livePlot) plot.templateFlags = setFlag(plot.templateFlags, Plot.Y_RANGE, gd.getNextBoolean());
 			boolean yLog = gd.getNextBoolean();
 			plot.setAxisXLog(xLog);
 			plot.setAxisYLog(yLog);
+                        plot.setLimits(linXMin, linXMax, linYMin, linYMax);
 			plot.updateImage();
 			if (Recorder.record) {
 				if (Recorder.scriptMode()) {
 					Recorder.recordCall("//plot = IJ.getImage().getProperty(Plot.PROPERTY_KEY)");
 					Recorder.recordCall("plot.setAxisXLog("+xLog+");");
 					Recorder.recordCall("plot.setAxisYLog("+yLog+");");
-					Recorder.recordCall("plot.setLimits("+IJ.d2s(currentMinMax[0],xDigits)+","+IJ.d2s(currentMinMax[1],xDigits)+","+IJ.d2s(currentMinMax[2],yDigits)+","+IJ.d2s(currentMinMax[3],yDigits)+");");
+					//Recorder.recordCall("plot.setLimits("+IJ.d2s(currentMinMax[0],xDigits)+","+IJ.d2s(currentMinMax[1],xDigits)+","+IJ.d2s(currentMinMax[2],yDigits)+","+IJ.d2s(currentMinMax[3],yDigits)+");");
+					Recorder.recordCall("plot.setLimits("+IJ.d2s(linXMin,xDigits)+","+IJ.d2s(linXMax,xDigits)+","+IJ.d2s(linYMin,yDigits)+","+IJ.d2s(linYMax,yDigits)+");");
 				} else {
 					Recorder.recordString("Plot.setLogScaleX("+xLog+");\n");
 					Recorder.recordString("Plot.setLogScaleY("+yLog+");\n");
-					Recorder.recordString("Plot.setLimits("+IJ.d2s(currentMinMax[0],xDigits)+","+IJ.d2s(currentMinMax[1],xDigits)+","+IJ.d2s(currentMinMax[2],yDigits)+","+IJ.d2s(currentMinMax[3],yDigits)+");\n");
+					//Recorder.recordString("Plot.setLimits("+IJ.d2s(currentMinMax[0],xDigits)+","+IJ.d2s(currentMinMax[1],xDigits)+","+IJ.d2s(currentMinMax[2],yDigits)+","+IJ.d2s(currentMinMax[3],yDigits)+");\n");
+					Recorder.recordString("Plot.setLimits("+IJ.d2s(linXMin,xDigits)+","+IJ.d2s(linXMax,xDigits)+","+IJ.d2s(linYMin,yDigits)+","+IJ.d2s(linYMax,yDigits)+");\n");
 				}
 			}
+		//n__ end PlotDialog
 		} else if (dialogType == AXIS_OPTIONS) {
 			int flags = plot.getFlags();
 			int columns = 2;
diff --git a/ij/gui/PlotWindow.java b/ij/gui/PlotWindow.java
index 1d45299..d9ab447 100644
--- a/ij/gui/PlotWindow.java
+++ b/ij/gui/PlotWindow.java
@@ -32,17 +32,13 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 	public static final int CROSS = Plot.CROSS;
 	/** Connect points with solid lines. */
 	public static final int LINE = Plot.LINE;
-	/** Save x-values only. To set, use Edit/Options/
-		Profile Plot Options. */
-	public static boolean saveXValues;
-	/** Automatically close window after saving values. To
-		set, use Edit/Options/Profile Plot Options. */
+	/** Write first X column when listing or saving. */
+	public static boolean saveXValues = true;
+	/** Automatically close window after saving values. To set, use Edit/Options/Plots. */
 	public static boolean autoClose;
-	/** Display the XY coordinates in a separate window. To
-		set, use Edit/Options/Profile Plot Options. */
+	/** Display the XY coordinates in a separate window. To set, use Edit/Options/Plots. */
 	public static boolean listValues;
-	/** Interpolate line profiles. To
-		set, use Edit/Options/Profile Plot Options. */
+	/** Interpolate line profiles. To set, use Edit/Options/Plots. */
 	public static boolean interpolate;
 	// default values for new installations; values will be then saved in prefs
 	private static final int WIDTH = 450;
@@ -100,7 +96,6 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 	// static initializer
 	static {
 		options = Prefs.getInt(OPTIONS, SAVE_X_VALUES);
-		saveXValues = (options&SAVE_X_VALUES)!=0;
 		autoClose = (options&AUTO_CLOSE)!=0;
 		listValues = (options&LIST_VALUES)!=0;
 		plotWidth = Prefs.getInt(PREFS_WIDTH, WIDTH);
@@ -291,7 +286,7 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 		menuItems[FREEZE] = addPopupItem(popupMenu, "Freeze Plot", true);
 		menuItems[HI_RESOLUTION] = addPopupItem(popupMenu, "High-Resolution Plot...");
 		popupMenu.addSeparator();
-		menuItems[PROFILE_PLOT_OPTIONS] = addPopupItem(popupMenu, "Profile & Plot Options...");
+		menuItems[PROFILE_PLOT_OPTIONS] = addPopupItem(popupMenu, "Plot Options...");
 		return popupMenu;
 	}
 
@@ -355,7 +350,7 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 		} else if (b==menuItems[HI_RESOLUTION])
 			new PlotDialog(plot, PlotDialog.HI_RESOLUTION).showDialog(this);
 		else if (b==menuItems[PROFILE_PLOT_OPTIONS])
-			IJ.doCommand("Profile Plot Options...");
+			IJ.doCommand("Plots...");
 		ic.requestFocus();	//have focus on the canvas, not the button, so that pressing the space bar allows panning
 	}
 
@@ -367,39 +362,47 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 		}
 	}
 
-	/** Updates the X and Y values when the mouse is moved and, if appropriate, shows/hides
-	 *	the overlay with the triangular buttons for changing the axis range limits
-	 *	Overrides mouseMoved() in ImageWindow. 
-	 *	@see ij.gui.ImageWindow#mouseMoved
-	 */
-	public void mouseMoved(int x, int y) {
-		super.mouseMoved(x, y);
-		if (plot==null) return;
-		if (coordinates!=null) {	//coordinate readout
-			String coords = plot.getCoordinates(x,y) + blankLabel;
-			coordinates.setText(coords.substring(0, blankLabel.length()));
-		}
-
-		//arrows for modifying the plot range
-		if (x<plot.leftMargin || y>plot.topMargin+plot.frameHeight) {
-			if (!rangeArrowsVisible && !plot.isFrozen())
-				showRangeArrows();
-			if (activeRangeArrow >= 0 && !rangeArrowRois[activeRangeArrow].contains(x,y)) {
-				rangeArrowRois[activeRangeArrow].setFillColor(Color.GRAY);
-				ic.repaint();			//de-highlight arrow where cursor has moved out
-				activeRangeArrow = -1;
-			}
-			if (activeRangeArrow < 0) { //highlight arrow below cursor (if any)
-				int i = getRangeArrowIndex(x,y);
-				if (i >= 0) {			//we have an arrow at cursor position
-					rangeArrowRois[i].setFillColor(Color.RED);
-					activeRangeArrow = i;
-					ic.repaint();
-				}
-			}
-		} else if (rangeArrowsVisible)
-			hideRangeArrows();
-	}
+//n__ begin mouseMoved
+    /**
+     * Updates the X and Y values when the mouse is moved and, if appropriate,
+     * shows/hides the overlay with the triangular buttons for changing the axis
+     * range limits Overrides mouseMoved() in ImageWindow.
+     *
+     * @see ij.gui.ImageWindow#mouseMoved
+     */
+    public void mouseMoved(int x, int y) {
+        super.mouseMoved(x, y);
+        if (plot == null)
+            return;
+        if (coordinates != null) {	//coordinate readout
+            String coords = plot.getCoordinates(x, y) + blankLabel;
+            coordinates.setText(coords.substring(0, blankLabel.length()));
+        }
+
+        //arrows for modifying the plot range
+        if (x < plot.leftMargin || y > plot.topMargin + plot.frameHeight) {
+            if (!rangeArrowsVisible && !plot.isFrozen())
+                showRangeArrows();
+            if (activeRangeArrow == 8)//it's the 'R' icon
+                coordinates.setText("Reset Range");
+            if (activeRangeArrow >= 0 && !rangeArrowRois[activeRangeArrow].contains(x, y)) {
+                rangeArrowRois[activeRangeArrow].setFillColor(Color.GRAY);
+                ic.repaint();			//de-highlight arrow where cursor has moved out
+                activeRangeArrow = -1;
+            }
+            if (activeRangeArrow < 0) { //highlight arrow below cursor (if any)
+                int i = getRangeArrowIndex(x, y);
+                if (i >= 0) {			//we have an arrow at cursor position
+
+                    rangeArrowRois[i].setFillColor(Color.RED);
+                    activeRangeArrow = i;
+                    ic.repaint();
+                }
+            }
+        } else if (rangeArrowsVisible)
+            hideRangeArrows();
+    }    
+    //n__ end mouseMoved
 
 	/** Called by PlotCanvas */
 	void mouseExited(MouseEvent e) {
@@ -431,40 +434,56 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 			plot.scroll(0, rotation*amount*Math.max(ic.imageHeight/50, 1));
 	}
 
-	/** Creates an overlay with triangular buttons for changing the axis range limits and shows it */
-	void showRangeArrows() {
-		if (imp == null) return;
-		hideRangeArrows(); //in case we have old arrows from a different plot size or so
-		rangeArrowRois = new Roi[4*2]; //4 arrows per axis
-		int i=0;
-		int height = imp.getHeight();
-		int arrowH = plot.topMargin < 14 ? 6 : 8; //height of arrows and distance between them; base is twice that value
-		float[] yP = new float[]{height-arrowH/2, height-3*arrowH/2, height-5*arrowH/2-0.1f};
-		for (float x : new float[]{plot.leftMargin, plot.leftMargin+plot.frameWidth}) { //create arrows for x axis
-			float[] x0 = new float[]{x-arrowH/2, x-3*arrowH/2-0.1f, x-arrowH/2};
-			rangeArrowRois[i++] = new PolygonRoi(x0, yP, 3, Roi.POLYGON);
-			float[] x1 = new float[]{x+arrowH/2, x+3*arrowH/2+0.1f, x+arrowH/2};
-			rangeArrowRois[i++] = new PolygonRoi(x1, yP, 3, Roi.POLYGON);
-		}
-		float[] xP = new float[]{arrowH/2-0.1f, 3*arrowH/2, 5*arrowH/2+0.1f};
-		for (float y : new float[]{plot.topMargin+plot.frameHeight, plot.topMargin}) { //create arrows for y axis
-			float[] y0 = new float[]{y+arrowH/2, y+3*arrowH/2+0.1f, y+arrowH/2};
-			rangeArrowRois[i++] = new PolygonRoi(xP, y0, 3, Roi.POLYGON);
-			float[] y1 = new float[]{y-arrowH/2, y-3*arrowH/2-0.1f, y-arrowH/2};
-			rangeArrowRois[i++] = new PolygonRoi(xP, y1, 3, Roi.POLYGON);
-		}
-		Overlay ovly = imp.getOverlay();
-		if (ovly == null)
-			ovly = new Overlay();
-		for (Roi roi : rangeArrowRois) {
-			roi.setFillColor(Color.GRAY);
-			ovly.add(roi);
-		}
-		imp.setOverlay(ovly);
-		ic.repaint();
-		rangeArrowsVisible = true;
-	}
-
+    //n__ begin showRangeArrows
+    /**
+     * Creates an overlay with triangular buttons for changing the axis range
+     * limits and shows it
+     */
+    void showRangeArrows() {
+        if (imp == null)
+            return;
+        hideRangeArrows(); //in case we have old arrows from a different plot size or so
+        rangeArrowRois = new Roi[4 * 2 + 1]; //4 arrows per axis plus 1 'Reset' icon 
+        int i = 0;
+        int height = imp.getHeight();
+        int arrowH = plot.topMargin < 14 ? 6 : 8; //height of arrows and distance between them; base is twice that value
+        float[] yP = new float[]{height - arrowH / 2, height - 3 * arrowH / 2, height - 5 * arrowH / 2 - 0.1f};
+
+        for (float x : new float[]{plot.leftMargin, plot.leftMargin + plot.frameWidth}) { //create arrows for x axis
+            float[] x0 = new float[]{x - arrowH / 2, x - 3 * arrowH / 2 - 0.1f, x - arrowH / 2};
+            rangeArrowRois[i++] = new PolygonRoi(x0, yP, 3, Roi.POLYGON);
+            float[] x1 = new float[]{x + arrowH / 2, x + 3 * arrowH / 2 + 0.1f, x + arrowH / 2};
+            rangeArrowRois[i++] = new PolygonRoi(x1, yP, 3, Roi.POLYGON);
+        }
+        float[] xP = new float[]{arrowH / 2 - 0.1f, 3 * arrowH / 2, 5 * arrowH / 2 + 0.1f};
+        for (float y : new float[]{plot.topMargin + plot.frameHeight, plot.topMargin}) { //create arrows for y axis
+            float[] y0 = new float[]{y + arrowH / 2, y + 3 * arrowH / 2 + 0.1f, y + arrowH / 2};
+            rangeArrowRois[i++] = new PolygonRoi(xP, y0, 3, Roi.POLYGON);
+            float[] y1 = new float[]{y - arrowH / 2, y - 3 * arrowH / 2 - 0.1f, y - arrowH / 2};
+            rangeArrowRois[i++] = new PolygonRoi(xP, y1, 3, Roi.POLYGON);
+        }
+        Font theFont = new Font("SansSerif", Font.BOLD, 13);
+
+        TextRoi txtRoi = new TextRoi(2, height - 20, 20, 18, " R ", theFont);
+        rangeArrowRois[8] = txtRoi;
+
+        Overlay ovly = imp.getOverlay();
+        if (ovly == null)
+            ovly = new Overlay();
+        for (Roi roi : rangeArrowRois) {
+            if (roi instanceof TextRoi) {
+                txtRoi.setStrokeColor(Color.WHITE);
+                txtRoi.setFillColor(Color.GRAY);
+            } else
+                roi.setFillColor(Color.GRAY);
+            ovly.add(roi);
+        }
+        imp.setOverlay(ovly);
+        ic.repaint();
+        rangeArrowsVisible = true;
+    }
+    //n__ end showRangeArrows
+          
 	void hideRangeArrows() {
 		if (imp == null || rangeArrowRois==null) return;
 		Overlay ovly = imp.getOverlay();
@@ -611,7 +630,6 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 			prefs.put(PREFS_FONT_SIZE, Integer.toString(fontSize));
 		}
 		int options = 0;
-		if (saveXValues) options |= SAVE_X_VALUES;
 		if (autoClose && !listValues) options |= AUTO_CLOSE;
 		if (listValues) options |= LIST_VALUES;
 		if (!interpolate) options |= INTERPOLATE; // true=0, false=1
@@ -734,6 +752,13 @@ public class PlotWindow extends ImageWindow implements ActionListener,	ItemListe
 		return plot;
 	}
 	
+	/** Freezes the active plot window. */
+	public static void freeze() {
+		Window win = WindowManager.getActiveWindow();
+		if (win!=null && (win instanceof PlotWindow))
+			((PlotWindow)win).getPlot().setFrozen(true);
+	}
+	
 }
 
 
diff --git a/ij/gui/PointRoi.java b/ij/gui/PointRoi.java
index e3cb2b8..a1b03af 100644
--- a/ij/gui/PointRoi.java
+++ b/ij/gui/PointRoi.java
@@ -34,11 +34,14 @@ public class PointRoi extends PolygonRoi {
 	private boolean showLabels;
 	private int type = HYBRID;
 	private int size = SMALL;
+	private static int defaultCounter;
 	private int counter;
 	private int nCounters = 1;
-	private int[] counters;
+	private short[] counters;
+	private short[] positions;
 	private int[] counts = new int[MAX_COUNTERS];
 	private ResultsTable rt;
+	private long lastPointTime;
 	
 	static {
 		setDefaultType((int)Prefs.get(TYPE_KEY, HYBRID));
@@ -99,7 +102,8 @@ public class PointRoi extends PolygonRoi {
 				r = (int)(r/mag);
 			imp.draw(x-r, y-r, 2*r, 2*r);
 		}
-		incrementCounter();
+		setCounter(Toolbar.getMultiPointMode()?defaultCounter:0);
+		incrementCounter(imp);
 		enlargeArrays(50);
 		if (Recorder.record && !Recorder.scriptMode()) 
 			Recorder.record("makePoint", x, y);
@@ -143,16 +147,19 @@ public class PointRoi extends PolygonRoi {
 		if (showLabels && nPoints>1) {
 			fontSize = 8;
 			fontSize += convertSizeToIndex(size);
-			if (mag>1.0)
-				fontSize = (int)(((mag-1.0)/3.0+1.0)*9.0);
 			if (fontSize>18) fontSize = 18;
 			font = new Font("SansSerif", Font.PLAIN, fontSize);
 			g.setFont(font);
 			if (fontSize>9)
 				Java2.setAntialiasedText(g, true);
 		}
-		for (int i=0; i<nPoints; i++)
-			drawPoint(g, xp2[i], yp2[i], i+1);
+		int slice = imp!=null&&positions!=null&&imp.getStackSize()>1?imp.getCurrentSlice():0;
+		if (Prefs.showAllPoints)
+			slice = 0;
+		for (int i=0; i<nPoints; i++) {
+			if (slice==0 || slice==positions[i])
+				drawPoint(g, xp2[i], yp2[i], i+1);
+		}
 		if (updateFullWindow) {
 			updateFullWindow = false;
 			imp.draw();
@@ -171,7 +178,7 @@ public class PointRoi extends PolygonRoi {
 			else
 				color = Color.cyan;
 		}
-		if (counters!=null)
+		if (nCounters>1 && counters!=null)
 			color = getColor(counters[n-1]);
 		if (type==HYBRID || type==CROSSHAIR) {
 			if (type==HYBRID)
@@ -203,8 +210,8 @@ public class PointRoi extends PolygonRoi {
 			else
 				g.fillRect(x-size2, y-size2, size, size);
 		}
-		if (showLabels) {
-			if (nPoints>1 && counters==null) {
+		if (showLabels && nPoints>1) {
+			if (nCounters==1) {
 				if (!colorSet)
 					g.setColor(color);
 				g.drawString(""+n, x+4, y+fontSize+2);
@@ -241,13 +248,13 @@ public class PointRoi extends PolygonRoi {
 	}
 	
 	/** Adds a point to this PointRoi. */
-	public void addPoint(double ox, double oy) {
+	public void addPoint(ImagePlus imp, double ox, double oy) {
 		if (nPoints==xpf.length)
 			enlargeArrays();
-		addPoint2(ox, oy);
+		addPoint2(imp, ox, oy);
 	}
 	
-	private void addPoint2(double ox, double oy) {
+	private void addPoint2(ImagePlus imp, double ox, double oy) {
 		double xbase = getXBase();
 		double ybase = getYBase();
 		xpf[nPoints] = (float)(ox-xbase);
@@ -255,30 +262,49 @@ public class PointRoi extends PolygonRoi {
 		xp2[nPoints] = (int)ox;
 		yp2[nPoints] = (int)oy;
 		nPoints++;
-		incrementCounter();
+		incrementCounter(imp);
+		lastPointTime = System.currentTimeMillis();
+	}
+	
+	/** Adds a point to this PointRoi. */
+	public PointRoi addPoint(double x, double y) {
+		addPoint(getImage(), x, y);
+		return this;
 	}
 
 	protected void deletePoint(int index) {
 		super.deletePoint(index);
 		if (index>=0 && index<=nPoints && counters!=null) {
 			counts[counters[index]]--;
-			for (int i=index; i<nPoints; i++)
+			for (int i=index; i<nPoints; i++) {
 				counters[i] = counters[i+1];
+				positions[i] = positions[i+1];
+			}
 			if (rt!=null && WindowManager.getFrame(getCountsTitle())!=null)
 				displayCounts();
 		}
 	}
 
-	private synchronized void incrementCounter() {
+	private synchronized void incrementCounter(ImagePlus imp) {
 		counts[counter]++;
-		if (counter!=0) {
-			if (counters==null)
-				counters = new int[nPoints*2];
-			counters[nPoints-1] = counter;
+		boolean isStack = imp!=null && imp.getStackSize()>1;
+		if (counter!=0 || isStack) {
+			if (counters==null) {
+				counters = new short[nPoints*2];
+				positions = new short[nPoints*2];
+			}
+			counters[nPoints-1] = (short)counter;
+			if (imp!=null)
+					positions[nPoints-1] = imp.getStackSize()>1?(short)imp.getCurrentSlice():0;
+			//if (positions[nPoints-1]==0 || positions[nPoints-1]==1 || counters[nPoints-1]==0)
+			//	IJ.log("incrementCounter: "+nPoints+" "+" "+positions[nPoints-1]+" "+counters[nPoints-1]+" "+imp);
 			if (nPoints+1==counters.length) {
-				int[] temp = new int[counters.length*2];
+				short[] temp = new short[counters.length*2];
 				System.arraycopy(counters, 0, temp, 0, counters.length);
 				counters = temp;
+				temp = new short[counters.length*2];
+				System.arraycopy(positions, 0, temp, 0, positions.length);
+				positions = temp;
 			}
 		}
 		if (rt!=null && WindowManager.getFrame(getCountsTitle())!=null)
@@ -289,6 +315,7 @@ public class PointRoi extends PolygonRoi {
 		for (int i=0; i<counts.length; i++)
 			counts[i] = 0;
 		counters = null;
+		positions = null;
 		PointToolOptions.update();
 	}
 	
@@ -349,11 +376,13 @@ public class PointRoi extends PolygonRoi {
 		return defaultType;
 	}
 	
+	/** Sets the point type (0=hybrid, 1=crosshair, 2=dot, 3=circle). */
 	public void setPointType(int type) {
 		if (type>=0 && type<types.length)
 			this.type = type;
 	}
 
+	/** Returns the point type (0=hybrid, 1=crosshair, 2=dot, 3=circle). */
 	public int getPointType() {
 		return type;
 	}
@@ -373,11 +402,13 @@ public class PointRoi extends PolygonRoi {
 		return convertSizeToIndex(defaultSize);
 	}
 
-	public void setSize(int index) {
-		if (index>=0 && index<sizes.length)
-			this.size = convertIndexToSize(index);
+	/** Sets the point size, where 'size' is 0-4. */
+	public void setSize(int size) {
+		if (size>=0 && size<sizes.length)
+			this.size = convertIndexToSize(size);
 	}
 
+	/** Returns the point size (0-4). */
 	public int getSize() {
 		return convertSizeToIndex(size);
 	}
@@ -444,23 +475,40 @@ public class PointRoi extends PolygonRoi {
 	}
 
 	public int getCounter() {
-		return this.counter;
+		return counter;
+	}
+
+	public static void setDefaultCounter(int counter) {
+		defaultCounter = counter;
 	}
 
 	public int getCount(int counter) {
-		return counts[counter];
+		if (counter==0 && counters==null)
+			return nPoints;
+		else
+			return counts[counter];
 	}
 	
 	public int[] getCounters() {
-		return counters;
+		if (counters==null)
+			return null;
+		int[] temp = new int[nPoints];
+		for (int i=0; i<nPoints; i++) {
+			temp[i] = (counters[i]&0xff) + ((positions[i]&0xffff)<<8);
+		}
+		return temp;
 	}
 
 	public void setCounters(int[] counters) {
 		if (counters!=null) {
-			this.counters = new int[counters.length*2];
-			for (int i=0; i<counters.length; i++) {
-				int counter = counters[i]&0xffff;
-				this.counters[i] = counter;
+			int n = counters.length;
+			this.counters = new short[n*2];
+			this.positions = new short[n*2];
+			for (int i=0; i<n; i++) {
+				int counter = counters[i]&0xff;
+				int position = (counters[i]>>8)&0xffff;
+				this.counters[i] = (short)counter;
+				this.positions[i] = (short)position;
 				if (counter<counts.length) {
 					counts[counter]++;
 					if (counter>nCounters-1)
@@ -470,17 +518,99 @@ public class PointRoi extends PolygonRoi {
 			IJ.setTool("multi-point");
 		}
 	}
-
+	
+	public int getPointPosition(int index) {
+		if (positions!=null && index<nPoints)
+			return positions[index];
+		else
+			return 0;
+	}
+	
 	public void displayCounts() {
+		ImagePlus imp = getImage();
+		String firstColumnHdr = "Slice";
 		rt = new ResultsTable();
-		for (int i=0; i<nCounters; i++) {
-			rt.setValue("Counter", i, i);
-			rt.setValue("Count", i, counts[i]);
+		int row = 0;
+		if (imp!=null && imp.getStackSize()>1 && positions!=null) {
+			int nChannels = 1;
+			int nSlices = 1;
+			int nFrames = 1;
+			boolean isHyperstack = false;
+			if (imp.isComposite() || imp.isHyperStack()) {
+				isHyperstack = true;
+				nChannels = imp.getNChannels();
+				nSlices = imp.getNSlices();
+				nFrames = imp.getNFrames();
+				int nDimensions = 2;
+				if (nChannels>1) nDimensions++;
+				if (nSlices>1) nDimensions++;
+				if (nFrames>1) nDimensions++;
+				if (nDimensions==3) {
+					isHyperstack = false;
+					if (nChannels>1)
+						firstColumnHdr = "Channel";
+				} else
+					firstColumnHdr = "Image";
+			}
+			int firstSlice = Integer.MAX_VALUE;
+			for (int i=0; i<nPoints; i++) {
+				if (positions[i]>0 && positions[i]<firstSlice)
+					firstSlice = positions[i];
+			}
+			if (firstSlice==Integer.MAX_VALUE)
+				firstSlice = 0;
+			int lastSlice = 0;
+			if (firstSlice>0) {
+				for (int i=0; i<nPoints; i++) {
+					if (positions[i]>lastSlice)
+						lastSlice = positions[i];
+				}
+			}
+			if (firstSlice>0) {
+				for (int slice=firstSlice; slice<=lastSlice; slice++) {
+					rt.setValue(firstColumnHdr, row, slice);
+					if (isHyperstack) {
+						int[] position = imp.convertIndexToPosition(slice);
+						if (nChannels>1)
+							rt.setValue("Channel", row, position[0]);
+						if (nSlices>1)
+							rt.setValue("Slice", row, position[1]);
+						if (nFrames>1)
+							rt.setValue("Frame", row, position[2]);
+					}
+					for (int counter=0; counter<nCounters; counter++) {
+						int count = 0;
+						for (int i=0; i<nPoints; i++) {
+							if (slice==positions[i] && counter==counters[i])
+								count++;
+						}
+						rt.setValue("Ctr "+counter, row, count);
+					}
+					row++;
+				}
+			}
 		}
+		rt.setValue(firstColumnHdr, row, "Total");
+		for (int i=0; i<nCounters; i++)
+			rt.setValue("Ctr "+i, row, counts[i]);
 		rt.showRowNumbers(false);
 		rt.show(getCountsTitle());
+		if (IJ.debugMode) debug();
 	}
 	
+	private void debug() {
+		FloatPolygon p = getFloatPolygon();
+		ResultsTable rt = new ResultsTable();
+		for (int i=0; i<nPoints; i++) {
+			rt.setValue("Counter", i, counters[i]);
+			rt.setValue("Position", i, positions[i]);
+			rt.setValue("X", i, p.xpoints[i]);
+			rt.setValue("Y", i, p.ypoints[i]);
+		}
+		rt.showRowNumbers(false);
+		rt.show(getCountsTitle());
+	}
+
 	private String getCountsTitle() {
 		return "Counts_"+(imp!=null?imp.getTitle():"");
 	}
@@ -488,8 +618,7 @@ public class PointRoi extends PolygonRoi {
 	public synchronized static String[] getCounterChoices() {
 		if (counterChoices==null) {
 			counterChoices = new String[MAX_COUNTERS];
-			counterChoices[0] = "Default";
-			for (int i=1; i<MAX_COUNTERS; i++)
+			for (int i=0; i<MAX_COUNTERS; i++)
 				counterChoices[i] = ""+i;
 		}
 		return counterChoices;
@@ -516,6 +645,50 @@ public class PointRoi extends PolygonRoi {
 		}
 	}
 
+	/** Returns a point index if it has been at least one second since
+		the last point was added and the specified screen coordinates are	 
+		inside or near a point, otherwise returns -1. */
+	public int isHandle(int sx, int sy) {
+		if ((System.currentTimeMillis()-lastPointTime)<1000L)
+			return -1;
+		int size = HANDLE_SIZE+this.size;
+		int halfSize = size/2;
+		int handle = -1;
+		int sx2, sy2;
+		int slice = !Prefs.showAllPoints&&positions!=null&&imp!=null&&imp.getStackSize()>1?imp.getCurrentSlice():0;
+		for (int i=0; i<nPoints; i++) {
+			if (slice!=0 && slice!=positions[i])
+				continue;
+			sx2 = xp2[i]-halfSize; sy2=yp2[i]-halfSize;
+			if (sx>=sx2 && sx<=sx2+size && sy>=sy2 && sy<=sy2+size) {
+				handle = i;
+				break;
+			}
+		}
+		return handle;
+	}
+
+	/** Returns a copy of this PointRoi. */
+	public synchronized Object clone() {
+		PointRoi r = (PointRoi)super.clone();
+		if (counters!=null) {
+			r.counters = new short[counters.length];
+			for (int i=0; i<counters.length; i++)
+				r.counters[i] = counters[i];
+		}
+		if (positions!=null) {
+			r.positions = new short[positions.length];
+			for (int i=0; i<positions.length; i++)
+				r.positions[i] = positions[i];
+		}
+		if (counts!=null) {
+			r.counts = new int[counts.length];
+			for (int i=0; i<counts.length; i++)
+				r.counts[i] = counts[i];
+		}
+		return r;
+	}
+
 	/** @deprecated */
 	public void setHideLabels(boolean hideLabels) {
 		this.showLabels = !hideLabels;
diff --git a/ij/gui/PolygonRoi.java b/ij/gui/PolygonRoi.java
index 568d4d6..f10b669 100644
--- a/ij/gui/PolygonRoi.java
+++ b/ij/gui/PolygonRoi.java
@@ -429,6 +429,8 @@ public class PolygonRoi extends Roi {
 	//Within correction circle, all vertices with sharp angles are removed.
 	//Norbert Vischer
 	protected void wipeBack() {
+		if (previousRoi!=null && previousRoi.modState==SUBTRACT_FROM_ROI)
+			return;
 		double correctionRadius = 20;
 		if (ic!=null)
 			correctionRadius /= ic.getMagnification();
diff --git a/ij/gui/Roi.java b/ij/gui/Roi.java
index d566943..87a91dc 100644
--- a/ij/gui/Roi.java
+++ b/ij/gui/Roi.java
@@ -1296,7 +1296,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable {
 		previousRoi.modState = NO_MODS;
 		PointRoi p1 = (PointRoi)previousRoi;
 		FloatPolygon poly = getFloatPolygon();
-		p1.addPoint(poly.xpoints[0], poly.ypoints[0]);
+		p1.addPoint(imp, poly.xpoints[0], poly.ypoints[0]);
 		imp.setRoi(p1);
 	}
 	
@@ -1326,7 +1326,8 @@ public class Roi extends Object implements Cloneable, java.io.Serializable {
 	 }
 
 	protected void showStatus() {
-		if (imp==null) return;
+		if (imp==null)
+			return;
 		String value;
 		if (state!=CONSTRUCTING && (type==RECTANGLE||type==POINT) && width<=25 && height<=25) {
 			ImageProcessor ip = imp.getProcessor();
@@ -1337,8 +1338,8 @@ public class Roi extends Object implements Cloneable, java.io.Serializable {
 			value = "";
 		Calibration cal = imp.getCalibration();
 		String size;
-		if (cal.scaled() && !IJ.altKeyDown())
-			size = ", w="+IJ.d2s(width*cal.pixelWidth)+", h="+IJ.d2s(height*cal.pixelHeight);
+		if (cal.scaled() && !(IJ.altKeyDown()||(state==NORMAL&&IJ.shiftKeyDown())))
+			size = ", w="+IJ.d2s(width*cal.pixelWidth)+" ("+width+"), h="+IJ.d2s(height*cal.pixelHeight)+" ("+height+")";
 		else
 			size = ", w="+width+", h="+height;
 		IJ.showStatus(imp.getLocationAsString(x,y)+size+value);
@@ -1955,6 +1956,24 @@ public class Roi extends Object implements Cloneable, java.io.Serializable {
 		return "";
 	}
 	
+	public ImageStatistics getStatistics() {
+		ImageProcessor ip = getMask();
+		Rectangle r = getBounds();
+		if (ip==null)
+			ip = new ByteProcessor(r.width, r.height);
+		Roi roi = (Roi)this.clone();
+		roi.setLocation(0.0, 0.0);
+		ip.setRoi(roi);
+		int params = Measurements.AREA+Measurements.CENTROID+Measurements.ELLIPSE
+			+Measurements.ELLIPSE+Measurements.CIRCULARITY+Measurements.SHAPE_DESCRIPTORS
+			+Measurements.PERIMETER+Measurements.RECT;
+		ImageStatistics stats = ImageStatistics.getStatistics(ip, params, null);
+		stats.mean = stats.min = stats.max = Double.NaN;
+		stats.xCentroid += r.x;
+		stats.yCentroid += r.y;
+		return stats;
+	}
+
 	public FloatPolygon getRotationCenter() {
 		FloatPolygon p = new FloatPolygon();
 		Rectangle2D r = getFloatBounds();
@@ -1970,6 +1989,32 @@ public class Roi extends Object implements Cloneable, java.io.Serializable {
 		xcenter = x;
 		ycenter = y;
 	}
+	
+	/* 
+	 * Returns the center of the of this selection's countour, or the
+	 * center of the bounding box of composite selections.<br> 
+	 * Author: Peter Haub (phaub at dipsystems.de)
+	 */
+	public double[] getContourCentroid() {
+		double xC=0, yC=0, lSum=0, x, y, dx, dy, l;
+		FloatPolygon poly = getFloatPolygon();
+		int nPoints = poly.npoints;
+		int n2 = nPoints-1;
+		for (int n1=0; n1<nPoints; n1++){
+			dx = poly.xpoints[n1] - poly.xpoints[n2];
+			dy = poly.ypoints[n1] - poly.ypoints[n2];
+			x = poly.xpoints[n2] + dx/2.0;
+			y = poly.ypoints[n2] + dy/2.0;
+			l = Math.sqrt(dx*dx + dy*dy);
+			xC += x*l;
+			yC += y*l;
+			lSum += l;
+			n2 = n1;
+		}
+		xC /= lSum;
+		yC /= lSum;
+		return new double[]{xC, yC};
+	}
 
 	/** Returns a hashcode for this Roi that typically changes 
 		if it is moved, even though it is still the same object. */
diff --git a/ij/gui/ShapeRoi.java b/ij/gui/ShapeRoi.java
index 5672955..e54754c 100644
--- a/ij/gui/ShapeRoi.java
+++ b/ij/gui/ShapeRoi.java
@@ -648,6 +648,8 @@ public class ShapeRoi extends Roi {
 	/**Returns the perimeter if this ShapeRoi can be decomposed 
 		into simple ROIs, otherwise returns zero. */
 	public double getLength() {
+		if (width==0 && height==0)
+			return 0.0;
 		double length = 0.0;
 		Roi[] rois = getRois();
 		ImagePlus imp2 = getImage();
diff --git a/ij/gui/StackWindow.java b/ij/gui/StackWindow.java
index a1dfb40..7a22c8e 100644
--- a/ij/gui/StackWindow.java
+++ b/ij/gui/StackWindow.java
@@ -181,7 +181,7 @@ public class StackWindow extends ImageWindow implements Runnable, AdjustmentList
 					slice = 1;
 				else if (slice>imp.getStack().getSize())
 					slice = imp.getStack().getSize();
-				imp.setSlice(slice);
+				setSlice(imp,slice);
 				imp.updateStatusbarValue();
 				SyncWindows.setZ(this, slice);
 			}
@@ -201,7 +201,7 @@ public class StackWindow extends ImageWindow implements Runnable, AdjustmentList
 	/** Displays the specified slice and updates the stack scrollbar. */
 	public void showSlice(int index) {
 		if (imp!=null && index>=1 && index<=imp.getStackSize()) {
-			imp.setSlice(index);
+			setSlice(imp,index);
 			SyncWindows.setZ(this, index);
 		}
 	}
@@ -216,7 +216,7 @@ public class StackWindow extends ImageWindow implements Runnable, AdjustmentList
 			zSelector.setMaximum(stackSize+1);
 		EventQueue.invokeLater(new Runnable() {
 			public void run() {
-				if (imp!=null)
+				if (imp!=null && zSelector!=null)
 					zSelector.setValue(imp.getCurrentSlice());
 			}
 		});
@@ -233,7 +233,7 @@ public class StackWindow extends ImageWindow implements Runnable, AdjustmentList
 				int s = slice;
 				slice = 0;
 				if (s!=imp.getCurrentSlice())
-					imp.setSlice(s);
+					setSlice(imp,s);
 			}
 		}
 	}
@@ -297,6 +297,14 @@ public class StackWindow extends ImageWindow implements Runnable, AdjustmentList
 		}
     }
     
+    private void setSlice(ImagePlus imp, int n) {
+		if (imp.isLocked()) {
+			IJ.beep();
+			IJ.showStatus("Image is locked");
+		} else
+			imp.setSlice(n);
+    }
+    
 	public boolean validDimensions() {
 		int c = imp.getNChannels();
 		int z = imp.getNSlices();
diff --git a/ij/io/DirectoryChooser.java b/ij/io/DirectoryChooser.java
index 3fcbe7f..35422bd 100644
--- a/ij/io/DirectoryChooser.java
+++ b/ij/io/DirectoryChooser.java
@@ -16,7 +16,7 @@ import javax.swing.filechooser.*;
  	/** Display a dialog using the specified title. */
  	public DirectoryChooser(String title) {
  		this.title = title;
-		if (IJ.isMacOSX())
+		if (IJ.isMacOSX() && !Prefs.useJFileChooser)
 			getDirectoryUsingFileDialog(title);
  		else {
 			String macroOptions = Macro.getOptions();
diff --git a/ij/io/FileSaver.java b/ij/io/FileSaver.java
index 5aaf65e..56ebb4c 100644
--- a/ij/io/FileSaver.java
+++ b/ij/io/FileSaver.java
@@ -166,16 +166,18 @@ public class FileSaver {
 	/** Saves the stack as a multi-image TIFF using the specified path.
 		 Equivalent to IJ.saveAsTiff(imp,path), which is more convenient. */
 	public boolean saveAsTiffStack(String path) {
-		if (fi.nImages==1)
-			{error("This is not a stack"); return false;}
+		if (fi.nImages==1) {
+			error("This is not a stack");
+			return false;
+		}
 		boolean virtualStack = imp.getStack().isVirtual();
 		if (virtualStack)
 			fi.virtualStack = (VirtualStack)imp.getStack();
 		fi.info = imp.getInfoProperty();
 		fi.description = getDescriptionString();
 		if (virtualStack) {
-			FileInfo fi = imp.getOriginalFileInfo();
-			if (path!=null && path.equals(fi.directory+fi.fileName)) {
+			FileInfo ofi = imp.getOriginalFileInfo();
+			if (path!=null && ofi!=null && path.equals(ofi.directory+ofi.fileName)) {
 				error("TIFF virtual stacks cannot be saved in place.");
 				return false;
 			}
@@ -190,7 +192,7 @@ public class FileSaver {
 					labels = new String[vs.getSize()];
 				labels[i-1] = label;
 			}
-			this.fi.sliceLabels = labels;
+			fi.sliceLabels = labels;
 		} else
 			fi.sliceLabels = imp.getStack().getSliceLabels();
 		fi.roi = RoiEncoder.saveAsByteArray(imp.getRoi());
diff --git a/ij/io/ImageReader.java b/ij/io/ImageReader.java
index a9d76bc..624bbe6 100644
--- a/ij/io/ImageReader.java
+++ b/ij/io/ImageReader.java
@@ -577,6 +577,7 @@ public class ImageReader {
 		if (fi.compression>FileInfo.COMPRESSION_NONE)
 			return readCompressedRGB48(in);
 		int channels = fi.samplesPerPixel;
+		if (channels==1) channels=3;
 		short[][] stack = new short[channels][nPixels];
 		DataInputStream dis = new DataInputStream(in);
 		int pixel = 0;
@@ -662,6 +663,7 @@ public class ImageReader {
 
 	Object readRGB48Planar(InputStream in) throws IOException {
 		int channels = fi.samplesPerPixel;
+		if (channels==1) channels=3;
 		Object[] stack = new Object[channels];
 		for (int i=0; i<channels; i++) 
 			stack[i] = read16bitImage(in);
diff --git a/ij/io/Opener.java b/ij/io/Opener.java
index 2e72c1f..d54c1f1 100644
--- a/ij/io/Opener.java
+++ b/ij/io/Opener.java
@@ -404,6 +404,8 @@ public class Opener {
 	 * @see ij.IJ#openImage(String)
 	*/
 	public ImagePlus openURL(String url) {
+		url = updateUrl(url);
+		if (IJ.debugMode) IJ.log("OpenURL: "+url);
 		ImagePlus imp = openCachedImage(url);
 		if (imp!=null)
 			return imp;
@@ -454,8 +456,18 @@ public class Opener {
 		} 
 	}
 	
+	/** Can't open imagej.nih.gov URLs due to encryption so redirect to mirror.nih.net. */
+	public static String updateUrl(String url) {
+		if (url==null || !url.contains("nih.gov"))
+			return url;
+		url = url.replace("imagej.nih.gov/ij", "mirror.imagej.net");
+		url = url.replace("rsb.info.nih.gov/ij", "mirror.imagej.net");
+		url = url.replace("rsbweb.nih.gov/ij", "mirror.imagej.net");
+		return url;
+	}
+	
 	private ImagePlus openCachedImage(String url) {
-		if (url==null || !url.contains("ij/images"))
+		if (url==null || !url.contains("/images"))
 			return null;
 		String ijDir = IJ.getDirectory("imagej");
 		if (ijDir==null)
diff --git a/ij/io/PluginClassLoader.java b/ij/io/PluginClassLoader.java
index 263b8d1..5f2572d 100644
--- a/ij/io/PluginClassLoader.java
+++ b/ij/io/PluginClassLoader.java
@@ -63,7 +63,7 @@ public class PluginClassLoader extends URLClassLoader {
 	}
 
 	private void addDirectory(File f) {
-		if (IJ.debugMode) IJ.log("PluginClassLoader.addDirectory: "+f);
+		//if (IJ.debugMode) IJ.log("PluginClassLoader.addDirectory: "+f);
 		try {
 			// Add first level subdirectories to search path
 			addURL(f.toURI().toURL());
@@ -82,7 +82,7 @@ public class PluginClassLoader extends URLClassLoader {
 
     private void addJar(File f) {
         if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) {
-			if (IJ.debugMode) IJ.log("PluginClassLoader.addJar: "+f);
+			//if (IJ.debugMode) IJ.log("PluginClassLoader.addJar: "+f);
             try {
                 addURL(f.toURI().toURL());
             } catch (MalformedURLException e) {
diff --git a/ij/io/RoiEncoder.java b/ij/io/RoiEncoder.java
index dccb804..97ea647 100644
--- a/ij/io/RoiEncoder.java
+++ b/ij/io/RoiEncoder.java
@@ -143,9 +143,10 @@ public class RoiEncoder {
 		}
 		
 		if (roi instanceof PointRoi) {
+			countersSize = 0;
 			counters = ((PointRoi)roi).getCounters();
 			if (counters!=null && counters.length>=n)
-				countersSize = n*8;
+				countersSize = n*4;
 		}
 		
 		data = new byte[HEADER_SIZE+HEADER2_SIZE+n*4+floatSize+roiNameSize+roiPropsSize+countersSize];
diff --git a/ij/macro/Functions.java b/ij/macro/Functions.java
index b0a6766..c0a7199 100644
--- a/ij/macro/Functions.java
+++ b/ij/macro/Functions.java
@@ -94,7 +94,7 @@ public class Functions implements MacroConstants, Measurements {
 	void doFunction(int type) {
 		switch (type) {
 			case RUN: doRun(); break;
-			case SELECT: IJ.selectWindow(getStringArg()); resetImage(); break;
+			case SELECT: IJ.selectWindow(getStringArg()); resetImage(); interp.selectCount++; break;
 			case WAIT: IJ.wait((int)getArg()); break;
 			case BEEP: interp.getParens(); IJ.beep(); break;
 			case RESET_MIN_MAX: interp.getParens(); IJ.resetMinAndMax(); resetImage(); break;
@@ -1141,8 +1141,12 @@ public class Functions implements MacroConstants, Measurements {
 		int col = rt.getColumnIndex(column);
 		if (rt.columnExists(col))
 			return rt.getStringValue(col, row);
-		else
-			return "null";
+		else {
+			String label = null;
+			if ("Label".equals(column))
+				label = rt.getLabel(row);
+			return label!=null?label:"null";
+		}
 	}
 
 	String getResultLabel() {
@@ -1152,7 +1156,12 @@ public class Functions implements MacroConstants, Measurements {
 		if (row<0 || row>=counter)
 			interp.error("Row ("+row+") out of range");
 		String label = rt.getLabel(row);
-		return label!=null?label:"";
+		if (label!=null)
+			return label;
+		else {
+			label = rt.getStringValue("Label", row);
+			return label!=null?label:"";
+		}
 	}
 
 	private ResultsTable getResultsTable(boolean reportErrors) {
@@ -1559,11 +1568,18 @@ public class Functions implements MacroConstants, Measurements {
 				return getWindowType();
 			} else if (lowercaseKey.equals("window.title")||lowercaseKey.equals("window.name")) {
 				return getWindowTitle();
+			} else if (lowercaseKey.equals("macro.filepath")) {
+				String path = Macro_Runner.getFilePath();
+				return path!=null?path:"null";
 			} else {
 				String value = "";
-				try {value = System.getProperty(key);}
-				catch (Exception e) {};
-				return value!=null?value:"";
+				try {
+					value = System.getProperty(key);
+				} catch (Exception e) {};
+				if (value==null)
+					return("Invalid key");
+				else
+					return value;
 			}
 			return "";
 	}
@@ -1624,8 +1640,8 @@ public class Functions implements MacroConstants, Measurements {
 	
 	String getImageInfo() {		
 		ImagePlus imp = getImage();
-		Info infoPlugin = new Info();
-		return infoPlugin.getImageInfo(imp, getProcessor());
+		ImageInfo infoPlugin = new ImageInfo();
+		return infoPlugin.getImageInfo(imp);
 	}
 
 	public String getDirectory() {
@@ -2016,6 +2032,9 @@ public class Functions implements MacroConstants, Measurements {
 		} else if (name.equals("getValues")) {
 			getPlotValues();
 			return;
+		} else if (name.equals("showValues")) {
+			showPlotValues();
+			return;
 		}
 		// the following commands work with a plot under construction or an image with a plot created previously
 		Plot currentPlot = plot;
@@ -2068,6 +2087,9 @@ public class Functions implements MacroConstants, Measurements {
 		} else if (name.equals("setColor")) {
 			setPlotColor(currentPlot);
 			return;
+		} else if (name.equals("setBackgroundColor")) {
+			setPlotBackgroundColor(currentPlot);
+			return;
 		} else if (name.equals("setFontSize")) {
 			setPlotFontSize(currentPlot, false);
 			return;
@@ -2157,6 +2179,19 @@ public class Functions implements MacroConstants, Measurements {
 		yvar.setArray(ya);
 	}
 
+	void showPlotValues() {
+		interp.getParens();
+		ImagePlus imp = getImage();
+		ImageWindow win = imp.getWindow();
+		if (win==null || !(win instanceof PlotWindow)) {
+			interp.error("No plot window");
+			return;
+		}
+		PlotWindow pw = (PlotWindow)win;
+		ResultsTable rt = pw.getResultsTable();
+		rt.show("Results");
+	}
+
 	void newPlot() {
 		String title = getFirstString();
 		String xLabel = getNextString();
@@ -2243,6 +2278,13 @@ public class Functions implements MacroConstants, Measurements {
 		interp.getRightParen();
 	}
 
+	void setPlotBackgroundColor(Plot plot) {
+		interp.getLeftParen();
+		Color color = getColor();
+		interp.getRightParen();
+		plot.setBackgroundColor(color);
+	}
+
 	void setPlotFontSize(Plot plot, boolean forAxisLabels) {
 		float size = (float)getFirstArg();
 		int style = -1;
@@ -2816,6 +2858,7 @@ public class Functions implements MacroConstants, Measurements {
 			interp.getRightParen();
 		}
 		resetImage();
+		interp.selectCount++;
 	}
 	
 	void selectImage(String title) {
@@ -4239,6 +4282,8 @@ public class Functions implements MacroConstants, Measurements {
 			state = getProcessor().isBinary();
 		else if (arg.indexOf("grayscale")!=-1)
 			state = getProcessor().isGrayscale();
+		else if (arg.startsWith("global"))
+			state = ImagePlus.getStaticGlobalCalibration()!=null;
 		else if (arg.indexOf("animated")!=-1) {
 			ImageWindow win = getImage().getWindow();
 			state = win!=null && (win instanceof StackWindow) && ((StackWindow)win).getAnimate();
@@ -4550,8 +4595,18 @@ public class Functions implements MacroConstants, Measurements {
 		String name = interp.tokenString;
 		if (name.equals("isHyperstack")||name.equals("isHyperStack"))
 			return getImage().isHyperStack()?1.0:0.0;
-		else if (name.equals("getDimensions"))
-			{getDimensions(); return Double.NaN;}
+		else if (name.equals("getDimensions")) {
+			getDimensions();
+			return Double.NaN;
+		} else if (name.equals("stopOrthoViews")) {
+			interp.getParens(); 
+			Orthogonal_Views.stop();
+			return Double.NaN;
+		} else if (name.equals("getOrthoViewsID")) {
+			interp.getParens(); 
+			return Orthogonal_Views.getImageID();
+		} else if (name.equals("setOrthoViews"))
+			return setOrthoViews();
 		ImagePlus imp = getImage();
 		if (name.equals("setPosition"))
 			{setPosition(imp); return Double.NaN;}
@@ -4601,6 +4656,16 @@ public class Functions implements MacroConstants, Measurements {
 		return Double.NaN;
 	}
 	
+	private double setOrthoViews() { 
+		int x = (int)getFirstArg();
+		int y = (int)getNextArg();
+		int z = (int)getLastArg();
+		Orthogonal_Views orthoViews = Orthogonal_Views.getInstance();
+		if (orthoViews!=null)
+			orthoViews.setCrossLoc(x, y, z);
+		return Double.NaN;
+	}
+	
 	void getStackUnits(Calibration cal) {
 		Variable x = getFirstVariable();
 		Variable y = getNextVariable();
@@ -4808,7 +4873,9 @@ public class Functions implements MacroConstants, Measurements {
 		}
 		waitForUserDialog = new WaitForUserDialog(title, text);
 		Interpreter instance = Interpreter.getInstance();
+		interp.waitingForUser = true;
 		waitForUserDialog.show();
+		interp.waitingForUser = false;
 		Interpreter.setInstance(instance); // works around bug caused by use of drawing tools
 		if (waitForUserDialog.escPressed())
 			throw new RuntimeException(Macro.MACRO_CANCELED);
diff --git a/ij/macro/Interpreter.java b/ij/macro/Interpreter.java
index b2c7b28..9591ae4 100644
--- a/ij/macro/Interpreter.java
+++ b/ij/macro/Interpreter.java
@@ -60,6 +60,9 @@ public class Interpreter implements MacroConstants {
 	boolean inLoop;
 	int loopDepth;
 	static boolean tempShowMode;
+	boolean waitingForUser;
+	int selectCount;
+
 	
 	static TextWindow arrayWindow;
 	int inspectStkIndex = -1;
@@ -1738,6 +1741,11 @@ public class Interpreter implements MacroConstants {
 			if (rt!=null && rt.size()>0)
 				rt.show("Results");
 		}
+		if (IJ.isMacOSX() && selectCount>0 && debugger==null) {
+			Frame frame = WindowManager.getFrontWindow();
+			if (frame!=null && (frame instanceof ImageWindow))
+				ImageWindow.setImageJMenuBar((ImageWindow)frame);
+		}
 	}
 	
 	/** Aborts currently running macro. */
@@ -2081,6 +2089,19 @@ public class Interpreter implements MacroConstants {
 	static void setTempShowMode(boolean mode) {
 		tempShowMode = mode;
 	}
+	
+	private static Interpreter lastInterp;
+	
+	public static boolean nonBatchMacroRunning() {
+		Interpreter interp = getInstance();
+		if (interp==null)
+			return false;
+		int count =  interp.selectCount;
+		if (interp==lastInterp)
+			interp.selectCount++;
+		lastInterp = interp;
+		return !interp.waitingForUser && interp.debugger==null && count>0 && !isBatchMode();
+	}
 
 } // class Interpreter
 
diff --git a/ij/measure/Calibration.java b/ij/measure/Calibration.java
index 21d68f8..8a84ddf 100644
--- a/ij/measure/Calibration.java
+++ b/ij/measure/Calibration.java
@@ -82,7 +82,8 @@ public class Calibration implements Cloneable {
 	public Calibration(ImagePlus imp) {
 		if (imp!=null) {
 			bitDepth = imp.getBitDepth();
-			invertedLut = imp.isInvertedLut();
+			if (bitDepth!=UNKNOWN)
+				invertedLut=imp.isInvertedLut();
 		}
 	}
 	
@@ -255,7 +256,7 @@ public class Calibration implements Cloneable {
  		if (imp==null)
  			return;
  		int type = imp.getType();
- 		int newBitDepth = imp.getBitDepth();
+		int newBitDepth = imp.getBitDepth();
  		if (newBitDepth==16 && imp.getLocalCalibration().isSigned16Bit()) {
 			double[] coeff = new double[2]; coeff[0] = -32768.0; coeff[1] = 1.0;
  			setFunction(Calibration.STRAIGHT_LINE, coeff, DEFAULT_VALUE_UNIT);
@@ -500,7 +501,8 @@ public class Calibration implements Cloneable {
 			+ ", f=" + function
  			+ ", nc=" + (coefficients!=null?""+coefficients.length:"null")
  			+ ", table=" + (cTable!=null?""+cTable.length:"null")
-			+ ", vunit=" + valueUnit;
+			+ ", vunit=" + valueUnit
+			+ ", bd=" + bitDepth;
    }
 }
 
diff --git a/ij/measure/ResultsTable.java b/ij/measure/ResultsTable.java
index cf5a32d..5f52e1e 100644
--- a/ij/measure/ResultsTable.java
+++ b/ij/measure/ResultsTable.java
@@ -648,12 +648,11 @@ public class ResultsTable implements Cloneable {
 	/** Sets the decimal places (digits to the right of decimal point)
 		that are used when this table is displayed. */
 	public synchronized void setPrecision(int precision) {
+		if (precision>9) precision=9;
 		this.precision = (short)precision;
-		if (this==Analyzer.getResultsTable()) {
-			for (int i=0; i<decimalPlaces.length; i++) {
-				if (!(decimalPlaces[i]==AUTO_FORMAT||decimalPlaces[i]==0))
-					decimalPlaces[i] = (short)precision;
-			}
+		for (int i=0; i<decimalPlaces.length; i++) {
+			if (decimalPlaces[i]!=AUTO_FORMAT)
+				decimalPlaces[i] = (short)precision;
 		}
 	}
 	
diff --git a/ij/plugin/AVI_Reader.java b/ij/plugin/AVI_Reader.java
index 3fa4ba5..de13bf9 100644
--- a/ij/plugin/AVI_Reader.java
+++ b/ij/plugin/AVI_Reader.java
@@ -90,8 +90,8 @@ import javax.imageio.ImageIO;
  *		- can read AVI-2 files with blank frames into a virtual stack
  *	 2013-10-29
  *		- can read MJPG files where the frames don't have the same pixel number as the overall video
- *   2015-09-28
- *      - reads most ImageJ AVI1 files with size>4 GB (incorrectly written by ImageJ versions before 1.50b)
+ *	 2015-09-28
+ *		- reads most ImageJ AVI1 files with size>4 GB (incorrectly written by ImageJ versions before 1.50b)
  *
  * The AVI format looks like this:
  * RIFF AVI					RIFF HEADER, AVI CHUNK					
@@ -178,11 +178,11 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	private final static int   MJPG_COMPRESSION	 = 0x47504a4d; //'MJPG' Motion JPEG, also reads compression of individual frames
 	private final static int   PNG_COMPRESSION	 = 0x20676e70; //'png ' PNG compression of individual frames
 	private final static int   PNG_COMPRESSION2	 = 0x20474e50; //'PNG ' PNG compression of individual frames
-	private final static int   PNG_COMPRESSION3	 = 0x05;       //BI_PNG PNG compression of individual frames
+	private final static int   PNG_COMPRESSION3	 = 0x05;	   //BI_PNG PNG compression of individual frames
 
-	private final static int   BITMASK24 = 0x10000;            //for 24-bit (in contrast to 8, 16,... not a bitmask)
-	private final static long  SIZE_MASK = 0xffffffffL;        //for conversion of sizes from unsigned int to long
-	private final static long  FOUR_GB   = 0x100000000L;       //2^32; above this size of data AVI 1 has a problem for sure
+	private final static int   BITMASK24 = 0x10000;			   //for 24-bit (in contrast to 8, 16,... not a bitmask)
+	private final static long  SIZE_MASK = 0xffffffffL;		   //for conversion of sizes from unsigned int to long
+	private final static long  FOUR_GB	 = 0x100000000L;	   //2^32; above this size of data AVI 1 has a problem for sure
 
 	// flags from AVI chunk header 
 	private final static int   AVIF_HASINDEX	 = 0x00000010;	// Index at end of file?
@@ -221,7 +221,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	private	 int			   lastFrameToRead = Integer.MAX_VALUE;
 	private	 int			   totalFramesFromIndex;//number of frames from 'AVI 2.0' indices
 	private	 boolean		   indexForCountingOnly;//don't read the index, only count int totalFramesFromIndex how many entries
-	private  boolean		   isOversizedAvi1;     //AVI-1 file > 4GB
+	private	 boolean		   isOversizedAvi1;		//AVI-1 file > 4GB
 	//derived from BitMapInfo
 	private	 int			   dataCompression;		//data compression type used
 	private	 boolean		   isPlanarFormat;		//I420 & YV12 formats: y frame, then u,v frames
@@ -237,7 +237,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	private	 boolean		   verbose = IJ.debugMode;
 	private	 long			   startTime;
 	private	 boolean		   aborting;
-	private  boolean		   displayDialog = true;
+	private	 boolean		   displayDialog = true;
 
 	//From AVI Header Chunk
 	private	 int			   dwMicroSecPerFrame;
@@ -301,7 +301,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 			return;
 		}
 		if (displayDialog && !showDialog(fileName))
-			return;  //ask for parameters
+			return;	 //ask for parameters
 		try {
 			ImageStack stack = makeStack(path, firstFrame, lastFrame, isVirtual, convertToGray, flipVertical);	//read data
 		} catch (Exception e) {
@@ -519,18 +519,18 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 				raFile.seek(headerPositionEnd);
 				moviPosition = findFourccAndSkip(FOURCC_movi, true, fileSize);	// go behind the 'movi' list
 				if (moviPosition<0)
-				    throw new Exception("AVI File has no movie data");
-                long positionBehindMovie = raFile.getFilePointer();
-                while (positionBehindMovie < fileSize-8) {
-                    if (verbose)
-                        IJ.log("searching for 'idx1' at 0x"+Long.toHexString(positionBehindMovie));
-                    raFile.seek(positionBehindMovie);
-                    if (positionBehindMovie > FOUR_GB)
-                        isOversizedAvi1 = true;
+					throw new Exception("AVI File has no movie data");
+				long positionBehindMovie = raFile.getFilePointer();
+				while (positionBehindMovie < fileSize-8) {
+					if (verbose)
+						IJ.log("searching for 'idx1' at 0x"+Long.toHexString(positionBehindMovie));
+					raFile.seek(positionBehindMovie);
+					if (positionBehindMovie > FOUR_GB)
+						isOversizedAvi1 = true;
 					nextPosition = findFourccAndRead(FOURCC_idx1, false, fileSize, false);
-					if (nextPosition >= 0)      //AVI-1 index 'idx1' found
-					    break;
-					positionBehindMovie += FOUR_GB;  //maybe position was wrong because it was a 32-bit number, but > 4GB?
+					if (nextPosition >= 0)		//AVI-1 index 'idx1' found
+						break;
+					positionBehindMovie += FOUR_GB;	 //maybe position was wrong because it was a 32-bit number, but > 4GB?
 				}
 			}
 			if (verbose)
@@ -617,18 +617,18 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 			long size = readInt() & SIZE_MASK;
 			nextPos = raFile.getFilePointer() + size;
 
-            if (nextPos>fileSize || nextPos>endPosition) {
-                IJ.log("AVI File Error: '"+fourccString(type)+"' @ 0x"+Long.toHexString(raFile.getFilePointer()-8)+" has invalid length");
-                return -1;
-            }
+			if (nextPos>fileSize || nextPos>endPosition) {
+				IJ.log("AVI File Error: '"+fourccString(type)+"' @ 0x"+Long.toHexString(raFile.getFilePointer()-8)+" has invalid length");
+				return -1;
+			}
 			if (isList && type == FOURCC_LIST)
 				type = readInt();
 			if (verbose)
-				IJ.log("Search for '"+fourccString(fourcc)+"', found "+fourccString(type)+"' "+posSizeString(nextPos-size, size));
+				IJ.log("Search for '"+fourccString(fourcc)+"', found "+fourccString(type)+"' data "+posSizeString(nextPos-size, size));
 			if (type==fourcc) {
 				contentOk = readContents(fourcc, nextPos);
 			} else if (verbose)
-				IJ.log("'"+fourccString(fourcc)+"', ignored");
+				IJ.log("'"+fourccString(type)+"', ignored");
 			raFile.seek(nextPos);
 			if (contentOk)
 				return nextPos;			//found and read, breaks the loop
@@ -652,7 +652,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 				type = readInt();
 			if (verbose)
 				IJ.log("Searching for (to skip) '"+fourccString(fourcc)+"', found "+fourccString(type)+
-						"' "+posSizeString(chunkPos, size));
+						"' data "+posSizeString(chunkPos, size));
 			raFile.seek(nextPos);
 			if (type == fourcc)
 				return chunkPos;		//found and skipped, breaks the loop
@@ -798,24 +798,22 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		}
 		if (bIndexType == AVI_INDEX_OF_INDEXES) {		// 'indx' points to other indices
 			if (wLongsPerEntry != 4) return;			//badly formed index, ignore it
-			for (int i=0;i<nEntriesInUse;i++) {
+			for (int i=0;i<nEntriesInUse;i++) {			//read all entries (each pointing to an ix00 index)
 				long qwOffset = readLong();
 				int dwSize = readInt();
-				int dwDuration = readInt(); //ignored; not trustworthy anyhow
-				if (verbose) {
-				 //		IJ.log("qwOffset:"+qwOffset);
-					IJ.log("   index data '" +fourccString(dwChunkId)+"' "+posSizeString(qwOffset,dwSize)+timeString());
-				}
-				long temp= raFile.getFilePointer();
-				raFile.seek(qwOffset);
+				int dwDuration = readInt();				//number of frames in ix00; ignored: not always trustworthy
+				if (verbose)
+					IJ.log("   indx entry: '" +fourccString(dwChunkId)+"' incl header "+posSizeString(qwOffset,dwSize)+timeString());
+				long nextIndxEntryPointer = raFile.getFilePointer();
+				raFile.seek(qwOffset);					//qwOffset & dwSize here include chunk header of ix00
 				findFourccAndRead(FOURCC_ix00, false, qwOffset+dwSize, true);
-				raFile.seek(temp);
+				raFile.seek(nextIndxEntryPointer);
 				if (frameNumber>lastFrameToRead) break;
 			}
 		} else if (bIndexType == AVI_INDEX_OF_CHUNKS) {
-		    if (verbose) {
-                IJ.log("readAvi2Index frameNumber="+frameNumber+" firstFrame="+firstFrame);
-			    if (indexForCountingOnly) IJ.log("<just counting frames, not interpreting index now>");
+			if (verbose) {
+				IJ.log("readAvi2Index frameNumber="+frameNumber+" firstFrame="+firstFrame);
+				if (indexForCountingOnly) IJ.log("<just counting frames, not interpreting index now>");
 			}
 			if (wLongsPerEntry != 2) return;				//badly formed index, ignore it
 			if (dwChunkId != type0xdb && dwChunkId != type0xdc) { //not the stream we search for? (should not happen)
@@ -835,8 +833,8 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 				if (isVirtual) IJ.showProgress((double)frameNumber/lastFrameToRead);
 				if (frameNumber >= firstFrame && dwSize>0) { //only valid frames (no blank frames)
 					frameInfos.add(new long[] {pos, dwSize, (long) frameNumber*dwMicroSecPerFrame});
-                    if (verbose)
-                        IJ.log("movie data "+frameNumber+" '"+fourccString(dwChunkId)+"' "+posSizeString(pos,dwSize)+timeString());
+					if (verbose)
+						IJ.log("movie data "+frameNumber+" '"+fourccString(dwChunkId)+"' "+posSizeString(pos,dwSize)+timeString());
 				}
 				frameNumber++;
 				if (frameNumber>lastFrameToRead) break;
@@ -848,7 +846,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 
 	/** Read AVI 1 index 'idx1' */
 	private void readOldFrameIndex(long endPosition) throws Exception, IOException {
-        //IJ.log("READ AVI 1 INDEX, isOversizedAvi1="+isOversizedAvi1);
+		//IJ.log("READ AVI 1 INDEX, isOversizedAvi1="+isOversizedAvi1);
 		int offset = -1;		//difference between absolute frame address and address given in idx1
 		int[] offsetsToTry = new int[] {0, (int)moviPosition}; // dwOffset may be w.r.t. file start or w.r.t. 'movi' list.
 		long lastFramePos = 0;
@@ -859,8 +857,8 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 			int dwFlags = readInt();
 			int dwOffset = readInt();
 			int dwSize = readInt();
-            //IJ.log("idx1: dwOffset=0x"+Long.toHexString(dwOffset));
-            //IJ.log("moviPosition=0x"+Long.toHexString(moviPosition));
+			//IJ.log("idx1: dwOffset=0x"+Long.toHexString(dwOffset));
+			//IJ.log("moviPosition=0x"+Long.toHexString(moviPosition));
 			if ((dwChunkId==type0xdb || dwChunkId==type0xdc) && dwSize>0) {
 				if (offset < 0) {		// find out what the offset refers to
 					long temp = raFile.getFilePointer();
@@ -886,8 +884,8 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 				lastFramePos = framePos;
 				if (frameNumber >= firstFrame) {
 					frameInfos.add(new long[]{framePos+8, dwSize, (long)frameNumber*dwMicroSecPerFrame});
-                    if (verbose)
-                        IJ.log("idx1 movie data '"+fourccString(dwChunkId)+"' "+posSizeString(framePos,dwSize)+timeString());
+					if (verbose)
+						IJ.log("idx1 movie data '"+fourccString(dwChunkId)+"' "+posSizeString(framePos,dwSize)+timeString());
 				}
 				frameNumber++;
 				if (frameNumber>lastFrameToRead) break;
@@ -1016,8 +1014,9 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		scanLineSize = isPlanarFormat ? 
 				(biWidth * biBitCount) / 8 : ((biWidth * biBitCount + 31) / 32) * 4;
 
-		// a value of biClrUsed=0 means we determine this based on the bits per pixel
-		if (readPalette && biClrUsed==0)
+		// a value of biClrUsed=0 means we determine this based on the bits per pixel, if there is a palette
+		long spaceForPalette  = endPosition-raFile.getFilePointer();
+		if (readPalette && biClrUsed==0 && spaceForPalette!=0)
 			biClrUsed = 1 << biBitCount;
 
 		if (verbose) {
@@ -1028,8 +1027,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		}
 
 		//read color palette
-		if (readPalette) {
-			long spaceForPalette  = endPosition-raFile.getFilePointer();
+		if (readPalette && biClrUsed > 0) {
 			if (verbose)
 				IJ.log("   Reading "+biClrUsed+" Palette colors: " + posSizeString(spaceForPalette));
 			if (spaceForPalette < biClrUsed*4)
@@ -1051,11 +1049,11 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	void readMovieData(long endPosition) throws Exception, IOException {
 		if (verbose)
 			IJ.log("MOVIE DATA "+posSizeString(endPosition-raFile.getFilePointer())+timeString()+
-		            "\nSearching for stream "+streamNumber+": '"+
+					"\nSearching for stream "+streamNumber+": '"+
 					fourccString(type0xdb)+"' or '"+fourccString(type0xdc)+"' chunks");
 		if (isVirtual) {
-			if (frameInfos==null)                       // we might have it already from reading the first chunk
-				frameInfos = new Vector<long[]>(lastFrameToRead);   // holds frame positions in file (for non-constant frame sizes, should hold long[] with pos and size)
+			if (frameInfos==null)						// we might have it already from reading the first chunk
+				frameInfos = new Vector<long[]>(lastFrameToRead);	// holds frame positions in file (for non-constant frame sizes, should hold long[] with pos and size)
 		} else if (stack==null)
 				stack = new ImageStack(dwWidth, biHeight);
 		while (true) {									//loop over all chunks
@@ -1064,9 +1062,9 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 			long size = readInt() & SIZE_MASK;
 			long pos = raFile.getFilePointer();
 			long nextPos = pos + size;
-            if (nextPos > endPosition && nextPos < fileSize-8 && fileSize > FOUR_GB) {
-                endPosition =  fileSize;                //looks like old ImageJ AVI 1.0 >4GB: wrong endPosition
-            }
+			if (nextPos > endPosition && nextPos < fileSize-8 && fileSize > FOUR_GB) {
+				endPosition =  fileSize;				//looks like old ImageJ AVI 1.0 >4GB: wrong endPosition
+			}
 			if ((type==type0xdb || type==type0xdc) && size>0) {
 				IJ.showProgress((double)frameNumber /lastFrameToRead);
 				if (verbose)
@@ -1381,7 +1379,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		return posSizeString(raFile.getFilePointer(), size);
 	}
 
-	private String posSizeString(long pos, long size) throws IOException {
+	private String posSizeString(long pos, long size) {
 		return "0x"+Long.toHexString(pos)+"-0x"+Long.toHexString(pos+size-1)+" ("+size+" Bytes)";
 	}
 
diff --git a/ij/plugin/Animator.java b/ij/plugin/Animator.java
index dede73d..f9d3aed 100644
--- a/ij/plugin/Animator.java
+++ b/ij/plugin/Animator.java
@@ -82,16 +82,17 @@ public class Animator implements PlugIn {
 	void stopAnimation() {
 		swin.setAnimate(false);
 		IJ.wait(500+(int)(1000.0/animationRate));
-		imp.unlock(); 
 	}
 
 	void startAnimation() {
+		if (imp.isLocked()) {
+			return;
+		}
 		int first=firstFrame, last=lastFrame;
 		if (first<1 || first>nSlices || last<1 || last>nSlices)
 			{first=1; last=nSlices;}
 		if (swin.getAnimate())
 			{stopAnimation(); return;}
-		imp.unlock(); // so users can adjust brightness/contrast/threshold
 		swin.setAnimate(true);
 		long time, nextTime=System.currentTimeMillis();
 		Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
@@ -253,8 +254,6 @@ public class Animator implements PlugIn {
 	}
 	
 	void nextSlice() {
-		if (!imp.lock())
-			return;
 		boolean hyperstack = imp.isDisplayedHyperStack();
 		int channels = imp.getNChannels();
 		int slices = imp.getNSlices();
@@ -281,12 +280,9 @@ public class Animator implements PlugIn {
 			swin.showSlice(slice);
 		}
 		imp.updateStatusbarValue();
-		imp.unlock();
 	}	
 	
 	void previousSlice() {
-		if (!imp.lock())
-			return;
 		boolean hyperstack = imp.isDisplayedHyperStack();
 		int channels = imp.getNChannels();
 		int slices = imp.getNSlices();
@@ -313,12 +309,9 @@ public class Animator implements PlugIn {
 			swin.showSlice(slice);
 		}
 		imp.updateStatusbarValue();
-		imp.unlock();
 	}
 
 	void changeSlice(int pn) {
-		if (!imp.lock())
-			return;
 		boolean hyperstack = imp.isDisplayedHyperStack();
 		int channels = imp.getNChannels();
 		int slices = imp.getNSlices();
@@ -353,7 +346,6 @@ public class Animator implements PlugIn {
 			swin.showSlice(slice);
 		}
 		imp.updateStatusbarValue();
-		imp.unlock();
 	}
 
 	void setSlice() {
diff --git a/ij/plugin/ChannelSplitter.java b/ij/plugin/ChannelSplitter.java
index 4eb239a..c6c16ee 100644
--- a/ij/plugin/ChannelSplitter.java
+++ b/ij/plugin/ChannelSplitter.java
@@ -20,6 +20,7 @@ public class ChannelSplitter implements PlugIn {
 			imp.setIgnoreFlush(true);
 			imp.close();
 			for (int i=0; i<channels.length; i++) {
+				channels[i].setIJMenuBar(i==channels.length-1);
 				channels[i].show();
 				if (z>1 || t>1)
 					channels[i].setPosition(1, z, t);
@@ -40,11 +41,13 @@ public class ChannelSplitter implements PlugIn {
 			{imp.unlock(); imp.changes=false; imp.close();}
 		ImagePlus rImp = new ImagePlus(title+" (red)", channels[0]);
 		rImp.setCalibration(cal);
+		rImp.setIJMenuBar(false);
 		rImp.show();
 		rImp.setSlice(pos);
 		if (IJ.isMacOSX()) IJ.wait(500);
 		ImagePlus gImp = new ImagePlus(title+" (green)", channels[1]);
 		gImp.setCalibration(cal);
+		gImp.setIJMenuBar(false);
 		gImp.show();
 		gImp.setSlice(pos);
 		if (IJ.isMacOSX()) IJ.wait(500);
diff --git a/ij/plugin/CircularRoiMaker.java b/ij/plugin/CircularRoiMaker.java
new file mode 100644
index 0000000..ceff4cb
--- /dev/null
+++ b/ij/plugin/CircularRoiMaker.java
@@ -0,0 +1,83 @@
+package ij.plugin;
+import ij.*;
+import ij.gui.*;
+import ij.measure.Calibration;
+import java.awt.*;
+
+/** This class implements the Process/FFT/Make Circular Selection command. */
+public class CircularRoiMaker implements PlugIn, DialogListener {
+	private static double saveRadius;
+	private double xcenter, ycenter, radius;
+	private boolean bAbort;
+	private ImagePlus imp;
+	private Calibration cal;
+
+	public void run(String arg) {
+		imp = IJ.getImage();
+		cal = imp.getCalibration();
+		int width = imp.getWidth();
+		int height = imp.getHeight();
+		xcenter = width/2;
+		ycenter = height/2;
+		boolean macro = Macro.getOptions()!=null;
+		radius = !macro&&saveRadius!=0.0?saveRadius:width/4;
+		if (radius>width/2)
+			radius = width/2;
+		if (radius>height/2)
+			radius = height/2;
+		showDialog();
+		if (!macro)
+			saveRadius = radius;
+
+	}
+	
+	private void showDialog() {
+		Roi roi = imp.getRoi();
+		drawRoi();
+		GenericDialog gd = new GenericDialog("Circular ROI");
+		gd.addSlider("Radius:", 0, imp.getWidth()/2, radius);
+		gd.addDialogListener(this);
+		gd.showDialog();
+		if (gd.wasCanceled()) {
+			if (roi==null)
+				imp.deleteRoi();
+			 else // restore initial ROI when cancelled
+				imp.setRoi(roi);
+		}
+	}
+	
+	public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
+		radius = gd.getNextNumber();
+		if (gd.invalidNumber())
+			return false;
+		drawRoi();
+		return true;
+	}
+	
+	private void drawRoi() {
+		double x = xcenter - radius;
+		double y = ycenter - radius;
+		Roi roi = new OvalRoi(x, y, radius*2.0, radius*2.0);
+		imp.setRoi(roi);
+		showRadius();
+	}
+		
+	private void showRadius() {
+		String units = cal.getUnits();
+		String s = " radius = ";
+		if (imp.getProperty("FHT")!=null) {
+			int width = imp.getWidth();
+			if (radius<1.0)
+				s += "Infinity/c";
+			else if (cal.scaled()) 
+				s += IJ.d2s((width/radius)*cal.pixelWidth,2) + " " + units + "/c";
+		   else
+				s += IJ.d2s(width/radius,2) + " p/c";
+		} else {
+			int digits = cal.pixelWidth==1.0?0:2;
+			s +=  IJ.d2s(radius*cal.pixelWidth,digits)+" "+units;
+		}
+		IJ.showStatus(s);
+	}
+
+}
diff --git a/ij/plugin/Clipboard.java b/ij/plugin/Clipboard.java
index 07862c5..bd5613d 100644
--- a/ij/plugin/Clipboard.java
+++ b/ij/plugin/Clipboard.java
@@ -44,6 +44,22 @@ public class Clipboard implements PlugIn, Transferable {
 	 		IJ.noImage();
 	}
 	
+	private ImagePlus flatten(ImagePlus imp) {
+		if (imp.getOverlay()!=null && !imp.getHideOverlay() && !imp.isHyperStack()) {
+			ImagePlus imp2 = imp;
+			Roi roi = imp.getRoi();
+			if (imp.getStackSize()>1) {
+				imp.deleteRoi();
+				int slice = imp.getCurrentSlice();
+				imp = new Duplicator().run(imp, slice, slice);
+			}
+			imp = imp.flatten();
+			imp.setRoi(roi);
+			imp2.setRoi(roi);
+		}
+		return imp;
+	}
+	
 	void paste() {
 		if (ImagePlus.getClipboard()==null)
 			showSystemClipboard();
@@ -124,6 +140,7 @@ public class Clipboard implements PlugIn, Transferable {
 			throw new UnsupportedFlavorException(flavor);
 		ImagePlus imp = WindowManager.getCurrentImage();
 		if (imp!=null) {
+			imp = flatten(imp);
 			ImageProcessor ip;
 			if (imp.isComposite()) {
 				ip = new ColorProcessor(imp.getImage());
diff --git a/ij/plugin/CommandFinder.java b/ij/plugin/CommandFinder.java
index 20a3229..1768b34 100644
--- a/ij/plugin/CommandFinder.java
+++ b/ij/plugin/CommandFinder.java
@@ -8,12 +8,18 @@
     they can be selected by selecting with the mouse and clicking
     "Run"; alternatively hitting the up or down arrows will move the
     keyboard focus to the list and the selected command can be run
-    with Enter.  Double-clicking on a command in the list should
-    also run the appropriate command.
+    with Enter. When the list has focus, it is also possible to use
+    keyboard "scrolling": E.g., pressing "H" will select the first
+    command starting with the char "H". Pressing "H" again will select
+    the next row starting with the char "H", etc., looping between all
+    "H" starting commands. Double-clicking on a command in the list
+    should also run the appropriate command.
 
     @author Mark Longair <mark-imagej at longair.net>
     @author Johannes Schindelin <johannes.schindelin at gmx.de>
     @author Curtis Rueden <ctrueden at wisc.edu>
+    @author Tiago Ferreira <tiago.ferreira at mail.mcgill.ca>
+
  */
 
 package ij.plugin;
@@ -267,6 +273,13 @@ public class CommandFinder implements PlugIn, ActionListener, WindowListener, Ke
 				int row = table.getSelectedRow();
 				if (row>=0)
 					runCommand(tableModel.getCommand(row));
+			/* Loop through the list using the arrow keys */
+			} else if (key == KeyEvent.VK_UP) {
+				if (table.getSelectedRow() == 0)
+					table.setRowSelectionInterval(tableModel.getRowCount() - 1, tableModel.getRowCount() - 1);
+			} else if (key == KeyEvent.VK_DOWN) {
+				if (table.getSelectedRow() == tableModel.getRowCount() - 1)
+					table.setRowSelectionInterval(0, 0);
 			}
 		}
 	}
@@ -327,10 +340,25 @@ public class CommandFinder implements PlugIn, ActionListener, WindowListener, Ke
 		}
 	}
 
-	public void run(String ignored) {
+	/**
+	 * Displays the Command Finder dialog. If a Command Finder window is
+	 * already being displayed and <tt>initialSearch</tt> contains a valid
+	 * query, it will be closed and a new one displaying the new search
+	 * will be rebuilt at the same screen location.
+	 *
+	 * @param initialSearch
+	 *            The search string that populates Command Finder's search
+	 *            field. It is ignored if contains an invalid query (ie, if
+	 *            it is either <tt>null</tt> or <tt>empty</tt>).
+	 */
+	public void run(String initialSearch) {
 		if (frame!=null) {
-			WindowManager.toFront(frame);
-			return;
+			if (initialSearch!=null && !initialSearch.isEmpty()) {
+				frame.dispose(); // Rebuild dialog with new search string
+			} else {
+				WindowManager.toFront(frame);
+				return;
+			}
 		}
 		commandsHash = new Hashtable();
 
@@ -387,8 +415,8 @@ public class CommandFinder implements PlugIn, ActionListener, WindowListener, Ke
 		closeCheckBox = new JCheckBox("Close window after running command", closeWhenRunning);
 		closeCheckBox.addItemListener(this);
 
-		JPanel northPanel = new JPanel();
-		northPanel.add(new JLabel("Search:"));
+		JPanel northPanel = new JPanel(new BorderLayout());
+		northPanel.add(new JLabel(" Search:"), BorderLayout.WEST);
 		prompt = new JTextField("", 20);
 		prompt.getDocument().addDocumentListener(new PromptDocumentListener());
 		prompt.addKeyListener(this);
@@ -407,8 +435,46 @@ public class CommandFinder implements PlugIn, ActionListener, WindowListener, Ke
 		table.addKeyListener(this);
 		table.addMouseListener(this);
 
+		// Auto-scroll table using keystrokes
+		table.addKeyListener(new KeyAdapter() {
+			public void keyTyped(final KeyEvent evt) {
+				if (evt.isControlDown() || evt.isMetaDown())
+					return;
+				final int nRows = tableModel.getRowCount();
+				final char ch = Character.toLowerCase(evt.getKeyChar());
+				if (!Character.isLetterOrDigit(ch)) {
+					return; // Ignore searches for non alpha-numeric characters
+				}
+				final int sRow = table.getSelectedRow();
+				for (int row = (sRow+1) % nRows; row != sRow; row = (row+1) % nRows) {
+					final String rowData = tableModel.getValueAt(row, 0).toString();
+					final char rowCh = Character.toLowerCase(rowData.charAt(0));
+					if (ch == rowCh) {
+						table.setRowSelectionInterval(row, row);
+						table.scrollRectToVisible(table.getCellRect(row, 0, true));
+						break;
+					}
+				}
+			}
+		});
+
+		// List the most useful shortcuts in a tooltip. Tooltips
+		// can be an annoyance so we'll increase delay times
+		ToolTipManager ttm = ToolTipManager.sharedInstance();
+		ttm.setInitialDelay(2 * ttm.getInitialDelay());
+		ttm.setReshowDelay(2 * ttm.getReshowDelay());
+		ttm.setDismissDelay(2 * ttm.getDismissDelay());
+		table.setToolTipText("<html>Shortcuts:<br>"
+				+ " ↑ ↓  Select items<br>"
+				+ " ↵  Open item<br>"
+				+ " A-Z  Alphabetic scroll<br>"
+				+ " ⌫ Activate search field</html>");
+
 		scrollPane = new JScrollPane(table);
-		populateList("");
+		if (initialSearch==null)
+			initialSearch = "";
+		prompt.setText(initialSearch);
+		populateList(initialSearch);
 		contentPane.add(scrollPane, BorderLayout.CENTER);
 
 		runButton = new JButton("Run");
@@ -424,7 +490,7 @@ public class CommandFinder implements PlugIn, ActionListener, WindowListener, Ke
 		JPanel southPanel = new JPanel();
 		southPanel.setLayout(new BorderLayout());
 
-		JPanel optionsPanel = new JPanel();
+		JPanel optionsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
 		optionsPanel.add(closeCheckBox);
 
 		JPanel buttonsPanel = new JPanel();
@@ -477,14 +543,13 @@ public class CommandFinder implements PlugIn, ActionListener, WindowListener, Ke
 	}
 	
 	private void closeWindow() {
-		frame.dispose();
+		if (frame!=null)
+			frame.dispose();
 	}
 
 	public void windowActivated(WindowEvent e) {
-		if (IJ.isMacintosh() && frame!=null) {
-			IJ.wait(10);
+		if (IJ.isMacOSX() && frame!=null)
 			frame.setMenuBar(Menus.getMenuBar());
-		}
 	}
 	
 	public void windowDeactivated(WindowEvent e) { }
diff --git a/ij/plugin/Compiler.java b/ij/plugin/Compiler.java
index f478cb3..8ec8519 100644
--- a/ij/plugin/Compiler.java
+++ b/ij/plugin/Compiler.java
@@ -1,19 +1,16 @@
 package ij.plugin;
-import java.awt.*;
-import java.io.*;
-import java.util.*;
 import ij.*;
 import ij.gui.*;
 import ij.io.*;
 import ij.plugin.frame.Editor;
 import ij.plugin.Macro_Runner;
-import ij.plugin.filter.*;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
+import ij.plugin.filter.PlugInFilter;
+import ij.plugin.filter.PlugInFilterRunner;
+import java.awt.Font;
 import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Locale;
+import java.io.*;
+import java.util.*;
+import javax.tools.*;
 
 /** Compiles and runs plugins using the javac compiler. */
 public class Compiler implements PlugIn, FilenameFilter {
@@ -56,15 +53,12 @@ public class Compiler implements PlugIn, FilenameFilter {
 			return;
 		}
 		if (!isJavac()) {
-			//boolean pluginClassLoader = this.getClass().getClassLoader()==IJ.getClassLoader();
-			//boolean contextClassLoader = Thread.currentThread().getContextClassLoader()==IJ.getClassLoader();
-			if (IJ.debugMode) IJ.log("javac not found: ");
+			if (IJ.debugMode) IJ.log("Compiler: javac not found");
 			if (!checkForUpdateDone) {
 				checkForUpdate("/plugins/compiler/Compiler.jar", "1.48c");
 				checkForUpdateDone = true;
 			}
 			Object compiler = IJ.runPlugIn("Compiler", dir+name);
-			if (IJ.debugMode) IJ.log("plugin compiler: "+compiler);
 			if (compiler==null) {
 				boolean ok = Macro_Runner.downloadJar("/plugins/compiler/Compiler.jar");
 				if (ok)
@@ -131,7 +125,8 @@ public class Compiler implements PlugIn, FilenameFilter {
 		Vector sources = new Vector();
 		sources.add(path);
 		
-		if (IJ.debugMode){
+		/*
+		if (IJ.debugMode) {
 			StringBuilder builder = new StringBuilder();
 			builder.append("javac");
 			for (int i=0; i< options.size(); i++){
@@ -144,6 +139,7 @@ public class Compiler implements PlugIn, FilenameFilter {
 			}
 			IJ.log(builder.toString());
 		}
+		*/
 		
 		boolean errors = true;
 		String s = "not compiled";
@@ -193,7 +189,7 @@ public class Compiler implements PlugIn, FilenameFilter {
 				addJars(path+list[i], sb);
 			else if (list[i].endsWith(".jar")&&(list[i].indexOf("_")==-1||list[i].equals("loci_tools.jar")||list[i].contains("3D_Viewer"))) {
 				sb.append(File.pathSeparator+path+list[i]);
-				if (IJ.debugMode) IJ.log("javac classpath: "+path+list[i]);
+				//if (IJ.debugMode) IJ.log("javac classpath: "+path+list[i]);
 			}
 		}
 	}
@@ -312,7 +308,7 @@ class PlugInExecuter implements Runnable {
 	}
 	
 	void runCompiledPlugin(String className) {
-		if (IJ.debugMode) IJ.log("runCompiledPlugin: "+className);
+		if (IJ.debugMode) IJ.log("Compiler: running "+className);
 		IJ.resetClassLoader();
 		ClassLoader loader = IJ.getClassLoader();
 		Object thePlugIn = null;
@@ -349,32 +345,19 @@ class PlugInExecuter implements Runnable {
 }
 
 abstract class CompilerTool {
+
 	public static class JavaxCompilerTool extends CompilerTool {
-		protected static Class charsetC;
-		protected static Class diagnosticListenerC;
-		protected static Class javaFileManagerC;
-		protected static Class toolProviderC;
 
 		public boolean compile(List sources, List options, StringWriter log) {
+			if (IJ.debugMode) IJ.log("Compiler: using javax.tool.JavaCompiler");
 			try {
-				Object javac = getJavac();
-
-				Class[] getStandardFileManagerTypes = new Class[] { diagnosticListenerC, Locale.class, charsetC };
-				Method getStandardFileManager = javac.getClass().getMethod("getStandardFileManager", getStandardFileManagerTypes);
-				Object fileManager = getStandardFileManager.invoke(javac, new Object[] { null, null, null });
-
-				Class[] getJavaFileObjectsFromStringsTypes = new Class[] { Iterable.class };
-				Method getJavaFileObjectsFromStrings = fileManager.getClass().getMethod("getJavaFileObjectsFromStrings", getJavaFileObjectsFromStringsTypes);
-				Object compilationUnits = getJavaFileObjectsFromStrings.invoke(fileManager, new Object[] { sources });
-
-				Class[] getTaskParamTypes = new Class[] { Writer.class, javaFileManagerC, diagnosticListenerC, Iterable.class, Iterable.class, Iterable.class };
-				Method getTask = javac.getClass().getMethod("getTask", getTaskParamTypes);
-				Object task = getTask.invoke(javac, new Object[] { log, fileManager, null, options, null, compilationUnits });
-
-				Method call = task.getClass().getMethod("call", new Class[0]);
-				Object result = call.invoke(task, new Object[0]);
-
-				return Boolean.TRUE.equals(result);
+				JavaCompiler javac = getJavac();
+				DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
+				StandardJavaFileManager fileManager = javac.getStandardFileManager(diagnostics, null, null);
+				Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(sources);
+				JavaCompiler.CompilationTask task =javac.getTask(log, fileManager, null, options, null, compilationUnits);
+    			fileManager.close();
+    			return task.call();
 			} catch (Exception e) {
 				PrintWriter printer = new PrintWriter(log);
 				e.printStackTrace(printer);
@@ -383,33 +366,17 @@ abstract class CompilerTool {
 			return false;
 		}
 
-		protected Object getJavac() throws Exception {
-			if (charsetC==null)
-				charsetC = Class.forName("java.nio.charset.Charset");
-			if (diagnosticListenerC==null)
-				diagnosticListenerC = Class.forName("javax.tools.DiagnosticListener");
-			if (javaFileManagerC==null)
-				javaFileManagerC = Class.forName("javax.tools.JavaFileManager");
-			if (toolProviderC==null)
-				toolProviderC = Class.forName("javax.tools.ToolProvider");
-			Method get = toolProviderC.getMethod("getSystemJavaCompiler", new Class[0]);
-			return get.invoke(null, new Object[0]);
+		protected JavaCompiler getJavac() throws Exception {
+			JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
+			return javac;
 		}
 	}
 
 	public static class LegacyCompilerTool extends CompilerTool {
 		protected static Class javacC;
 
-		boolean areErrors(String s) {
-			boolean errors = s != null && s.length() > 0;
-			if (errors && s.indexOf("1 warning") > 0 && s.indexOf("[deprecation] show()") > 0)
-				errors = false;
-			// if(errors&&s.startsWith("Note:com.sun.tools.javac")&&s.indexOf("error")==-1)
-			// errors = false;
-			return errors;
-		}
-
 		public boolean compile(List sources, List options, StringWriter log) {
+			if (IJ.debugMode) IJ.log("Compiler: using com.sun.tools.javac");
 			try {
 				final String[] args = new String[sources.size() + options.size()];
 				int argsIndex = 0;
@@ -417,13 +384,13 @@ abstract class CompilerTool {
 					args[argsIndex++] = (String) options.get(optionsIndex);
 				for (int sourcesIndex = 0; sourcesIndex < sources.size(); sourcesIndex++)
 					args[argsIndex++] = (String) sources.get(sourcesIndex);
+				PrintWriter printer = new PrintWriter(log);
 				Object javac = getJavac();
 				Class[] compileTypes = new Class[] { String[].class, PrintWriter.class };
 				Method compile = javacC.getMethod("compile", compileTypes);
-				PrintWriter printer = new PrintWriter(log);
 				Object result = compile.invoke(javac, new Object[] { args, printer });
 				printer.flush();
-				return Integer.valueOf(0).equals(result) | areErrors(log.toString());
+				return Integer.valueOf(0).equals(result);
 			} catch (Exception e) {
 				e.printStackTrace(new PrintWriter(log));
 			}
@@ -438,16 +405,14 @@ abstract class CompilerTool {
 	}
 
 	public static CompilerTool getDefault() {
-		CompilerTool javax = new JavaxCompilerTool();
-		if (javax.isSupported()) {
-			if (IJ.debugMode) IJ.log("javac: using javax.tool.JavaCompiler");
-			return javax;
+		if (IJ.isJava16()) {
+			CompilerTool javax = new JavaxCompilerTool();
+			if (javax.isSupported())
+				return javax;
 		}
 		CompilerTool legacy = new LegacyCompilerTool();
-		if (legacy.isSupported()) {
-			if (IJ.debugMode) IJ.log("javac: using com.sun.tools.javac");
+		if (legacy.isSupported())
 			return legacy;
-		}
 		return null;
 	}
 
diff --git a/ij/plugin/Converter.java b/ij/plugin/Converter.java
index 6e64270..2be8ebe 100644
--- a/ij/plugin/Converter.java
+++ b/ij/plugin/Converter.java
@@ -14,12 +14,15 @@ public class Converter implements PlugIn {
 	public void run(String arg) {
 		imp = WindowManager.getCurrentImage();
 		if (imp!=null) {
-			if (imp.isComposite() && arg.equals("RGB Color") && !imp.getStack().isRGB() && !imp.getStack().isHSB() && !imp.getStack().isLab())
+			if (imp.isComposite() && arg.equals("RGB Color") && !imp.getStack().isRGB() && !imp.getStack().isHSB() && !imp.getStack().isLab()) {
 				(new RGBStackConverter()).run("");
-			else if (imp.lock()) {
+				imp.setTitle(imp.getTitle()); // updates size in Window menu
+			} else if (imp.lock()) {
 				convert(arg);
 				imp.unlock();
-			}
+				imp.setTitle(imp.getTitle());
+			} else 
+				IJ.log("<<Converter: image is locked ("+imp+")>>");
 		} else
 			IJ.noImage();
 	}
diff --git a/ij/plugin/DragAndDrop.java b/ij/plugin/DragAndDrop.java
index 7be5481..37cd0a1 100644
--- a/ij/plugin/DragAndDrop.java
+++ b/ij/plugin/DragAndDrop.java
@@ -2,6 +2,7 @@ package ij.plugin;
 import ij.*;
 import ij.gui.*;
 import ij.io.*;
+import ij.process.ImageProcessor;
 import java.io.*;
 import java.awt.Point;
 import java.awt.datatransfer.*;
@@ -146,6 +147,12 @@ public class DragAndDrop implements PlugIn, DropTargetListener, Runnable {
 			Iterator iterator = this.iterator;
 			while(iterator.hasNext()) {
 				Object obj = iterator.next();
+				String str = ""+obj;
+				if (str!=null && str.startsWith("https:/")) {
+					if (!str.startsWith("https://"))
+						str = str.replace("https:/", "http://");
+					obj = str;
+				}
 				if (obj!=null && (obj instanceof String))
 					openURL((String)obj);
 				else
@@ -177,7 +184,11 @@ public class DragAndDrop implements PlugIn, DropTargetListener, Runnable {
 							(new FileInfoVirtualStack()).run(path);
 						else if (openAsVirtualStack && (path.endsWith(".avi")||path.endsWith(".AVI")))
 							IJ.run("AVI...", "open=["+path+"] use");
-						else
+						else if (openAsVirtualStack && (path.endsWith(".txt"))) {
+							ImageProcessor ip = (new TextReader()).open(path);
+							if (ip!=null)
+								new ImagePlus(f.getName(),ip).show();
+						} else
 							(new Opener()).openAndAddToRecent(path);
 						OpenDialog.setLastDirectory(f.getParent()+File.separator);
 						OpenDialog.setLastName(f.getName());
@@ -223,7 +234,11 @@ public class DragAndDrop implements PlugIn, DropTargetListener, Runnable {
 				for (int k=0; k<names.length; k++) {
 					if (!names[k].startsWith(".")) {
 						IJ.redirectErrorMessages(true);
-						(new Opener()).open(path + names[k]);
+						ImagePlus imp = IJ.openImage(path+names[k]);
+						if (imp!=null) {
+							imp.setIJMenuBar(k==names.length-1);
+							imp.show();
+						}
 						IJ.redirectErrorMessages(false);
 					}
 				}
diff --git a/ij/plugin/Grid.java b/ij/plugin/Grid.java
new file mode 100644
index 0000000..3dc8bd8
--- /dev/null
+++ b/ij/plugin/Grid.java
@@ -0,0 +1,263 @@
+package ij.plugin;
+import ij.*;
+import ij.process.*;
+import ij.gui.*;
+import ij.measure.*;
+import ij.util.Tools;
+import java.awt.*;
+import java.awt.geom.*;
+import java.util.*;
+
+/** This class implements the Analyze/Tools/Grid command. */
+public class Grid implements PlugIn, DialogListener {
+	private static final String OPTIONS = "grid.options";
+	private static final String GRID = "|GRID|";
+	private static double crossSize = 0.1;
+	private static String[] colors = {"Red","Green","Blue","Magenta","Cyan","Yellow","Orange","Black","White"};
+	private final static int LINES=0, HLINES=1, CROSSES=2, POINTS=3, CIRCLES=4, NONE=4;
+	private static String[] types = {"Lines","Horizontal Lines", "Crosses", "Points", "Circles", "None"};
+	private Random random = new Random(System.currentTimeMillis());
+	private ImagePlus imp;
+	private double tileWidth, tileHeight;
+	private int xstart, ystart;
+	private int linesV, linesH;
+	private double pixelWidth=1.0, pixelHeight=1.0;
+	private String units = "pixels";
+	private boolean isMacro;
+	private Roi gridOnEntry;
+
+	private String type = types[LINES];
+	private double areaPerPoint;
+	private static double saveAreaPerPoint;
+	private String color = "Cyan";
+	private boolean bold;
+	private boolean randomOffset;
+
+	public void run(String arg) {
+		imp = IJ.getImage();
+		Overlay overlay = imp.getOverlay();
+		int index = overlay!=null?overlay.getIndex(GRID):-1;
+		if (index>=0)
+			gridOnEntry = overlay.get(index);
+		if (showDialog() && !isMacro)
+			saveSettings();
+	}
+		
+	// http://stackoverflow.com/questions/30654203/how-to-create-a-circle-using-generalpath-and-apache-poi
+	private void drawCircles(double size) {
+		double R  = size*tileWidth;
+		if (R<1) R =1;
+		if (bold && type.equals(types[POINTS])) R*=1.5;
+		double kappa = 0.5522847498f;
+		GeneralPath path = new GeneralPath();
+		for(int h=0; h<linesV; h++) {
+			for(int v=0; v<linesH; v++) {
+				double x = xstart+h*tileWidth;
+				double y = ystart+v*tileHeight;
+				path.moveTo(x, y-R);
+				path.curveTo(x+R*kappa, y-R, x+R, y-R*kappa, x+R, y);
+				path.curveTo(x+R, y+R*kappa, x+R*kappa, y+R, x, y+R );
+				path.curveTo(x-R*kappa, y+R, x-R, y+R*kappa, x-R, y);
+				path.curveTo(x-R, y-R*kappa, x-R*kappa, y-R, x, y-R );
+				path.closePath();
+			}
+		}
+		drawGrid(path);
+	}
+
+	private void drawCrosses() {
+		GeneralPath path = new GeneralPath();
+		double arm  = crossSize*tileWidth;
+		if (arm<3) arm=3;
+		for(int h=0; h<linesV; h++) {
+			for(int v=0; v<linesH; v++) {
+				double x = xstart+h*tileWidth;
+				double y = ystart+v*tileHeight;
+				path.moveTo(x-arm, y);
+				path.lineTo(x+arm, y);
+				path.moveTo(x, y-arm);
+				path.lineTo(x, y+arm);
+			}
+		}
+		drawGrid(path);
+	}
+
+	private void drawLines() {
+		GeneralPath path = new GeneralPath();
+		int width = imp.getWidth();
+		int height = imp.getHeight();
+		for(int i=0; i<linesV; i++) {
+			float xoff = (float)(xstart+i*tileWidth);
+			path.moveTo(xoff,0f);
+			path.lineTo(xoff, height);
+		}
+		for(int i=0; i<linesH; i++) {
+			float yoff = (float)(ystart+i*tileHeight);
+			path.moveTo(0f, yoff);
+			path.lineTo(width, yoff);
+		}
+		drawGrid(path);
+	}
+
+	private void drawHorizontalLines() {
+		GeneralPath path = new GeneralPath();
+		int width = imp.getWidth();
+		int height = imp.getHeight();
+		for(int i=0; i<linesH; i++) {
+			float yoff = (float)(ystart+i*tileHeight);
+			path.moveTo(0f, yoff);
+			path.lineTo(width, yoff);
+		}
+		drawGrid(path);
+	}
+
+	void drawGrid(Shape shape) {
+		if (shape==null) {
+			Overlay overlay = imp.getOverlay();
+			if (overlay!=null) {
+				if (overlay.size()>1) {
+					overlay.remove(GRID);
+					imp.draw();
+				} else
+					imp.setOverlay(null);
+			}
+		} else {
+			Roi roi = new ShapeRoi(shape);
+			roi.setStrokeColor(Colors.getColor(color,Color.cyan));
+			if (bold && linesV*linesH<5000) {
+				ImageCanvas ic = imp.getCanvas();
+				double mag = ic!=null?ic.getMagnification():1.0;
+				double width = 2.0;
+				if (mag<1.0)
+					width = width/mag;
+				roi.setStrokeWidth(width);
+			}
+			IJ.showStatus(linesV*linesH+" nodes");
+			Overlay overlay = imp.getOverlay();
+			if (overlay!=null)
+				overlay.remove(GRID);
+			else
+				overlay = new Overlay();
+			overlay.add(roi, GRID);
+			imp.setOverlay(overlay);
+		}
+	}
+
+	private boolean showDialog() {
+		isMacro = Macro.getOptions()!=null;
+		if (!isMacro)
+			getSettings();
+		int width = imp.getWidth();
+		int height = imp.getHeight();
+		Calibration cal = imp.getCalibration();
+		int places;
+		if (cal.scaled()) {
+			pixelWidth = cal.pixelWidth;
+			pixelHeight = cal.pixelHeight;
+			units = cal.getUnits();
+			places = 2;
+		} else {
+			pixelWidth = 1.0;
+			pixelHeight = 1.0;
+			units = "pixels";
+			places = 0;
+		}
+		if (areaPerPoint==0.0)
+			areaPerPoint = (width*cal.pixelWidth*height*cal.pixelHeight)/81.0; // default to 9x9 grid
+		GenericDialog gd = new GenericDialog("Grid...");
+		gd.addChoice("Grid type:", types, type);
+		gd.addNumericField("Area per point:", areaPerPoint, places, 6, units+"^2");
+		gd.addChoice("Color:", colors, color);
+		gd.addCheckbox("Bold", bold);
+		gd.addCheckbox("Random offset", randomOffset);
+		gd.addDialogListener(this);
+		dialogItemChanged(gd, null);
+		gd.showDialog();
+		if (gd.wasCanceled()) {
+			Overlay overlay = imp.getOverlay();
+			if (overlay!=null && gridOnEntry!=null) {
+				overlay.remove(GRID);
+				overlay.add(gridOnEntry);
+				imp.draw();
+			} else
+				drawGrid(null);
+			return false;
+		} else
+			return true;
+	}
+
+	public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
+		int width = imp.getWidth();
+		int height = imp.getHeight();
+		type = gd.getNextChoice();
+		areaPerPoint = gd.getNextNumber();
+		color = gd.getNextChoice();
+		bold = gd.getNextBoolean();
+		randomOffset = gd.getNextBoolean();
+		double minArea= (width*height)/50000.0;
+		if (type.equals(types[CROSSES])&&minArea<50.0)
+			minArea = 50.0;
+		else if (minArea<16)
+			minArea = 16.0;
+		if (areaPerPoint/(pixelWidth*pixelHeight)<minArea) {
+			String err = "\"Area per Point\" too small";
+			if (gd.wasOKed())
+				IJ.error("Grid", err);
+			else
+				IJ.showStatus(err);
+			return true;
+		}
+		double tileSize = Math.sqrt(areaPerPoint);
+		tileWidth = tileSize/pixelWidth;
+		tileHeight = tileSize/pixelHeight;
+		if (randomOffset) {
+			xstart = (int)(random.nextDouble()*tileWidth);
+			ystart = (int)(random.nextDouble()*tileHeight);
+		} else {
+			xstart = (int)(tileWidth/2.0+0.5);
+			ystart = (int)(tileHeight/2.0+0.5);
+		}
+		linesV = (int)((width-xstart)/tileWidth)+1; 
+		linesH = (int)((height-ystart)/tileHeight)+1;
+		if (gd.invalidNumber())
+			return true;
+		drawGrid();
+        	return true;
+	}
+
+	private void drawGrid() {
+		if (type.equals(types[LINES]))
+			drawLines();
+		else if (type.equals(types[HLINES]))
+			drawHorizontalLines();
+		else if (type.equals(types[CROSSES]))
+			drawCrosses();
+		else  if (type.equals(types[POINTS]))
+			drawCircles(0.01);
+		else  if (type.equals(types[CIRCLES]))
+			drawCircles(0.1);
+		else
+			drawGrid(null);
+	}
+	
+	private void getSettings() {
+		String prefs = Prefs.get(OPTIONS, "Lines,Cyan,-");
+		String[] options = Tools.split(prefs, ",");
+		if (options.length>=3) {
+			type = options[0];
+			if ("None".equals(type))
+				type = types[LINES];
+			areaPerPoint = saveAreaPerPoint;
+			color = options[1];
+			bold = options[2].contains("bold");
+			randomOffset = options[2].contains("random");
+		}
+	}
+	
+	private void saveSettings() {
+		String options = type+","+color+","+(bold?"bold":"")+" "+(randomOffset?"random":"");
+		Prefs.set(OPTIONS, options);
+		saveAreaPerPoint = areaPerPoint;
+	}
+
+}
diff --git a/ij/plugin/GroupedZProjector.java b/ij/plugin/GroupedZProjector.java
index 8004ded..1991843 100644
--- a/ij/plugin/GroupedZProjector.java
+++ b/ij/plugin/GroupedZProjector.java
@@ -34,12 +34,14 @@ public class GroupedZProjector implements PlugIn {
 	public ImagePlus groupZProject(ImagePlus imp, int method, int groupSize) {
 		if (method<0 || method>=ZProjector.METHODS.length)
 			return null;
+		int[] dim = imp.getDimensions();
 		imp.setDimensions(1, groupSize, imp.getStackSize()/groupSize);
 		ZProjector zp = new ZProjector(imp);
 		zp.setMethod(method);
 		zp.setStartSlice(1);
 		zp.setStopSlice(groupSize);
 		zp.doHyperStackProjection(true);
+		imp.setDimensions(dim[2], dim[3], dim[4]);
 		return zp.getProjection();
 	}
 	
diff --git a/ij/plugin/filter/Info.java b/ij/plugin/ImageInfo.java
similarity index 88%
copy from ij/plugin/filter/Info.java
copy to ij/plugin/ImageInfo.java
index c4efbbe..b695941 100644
--- a/ij/plugin/filter/Info.java
+++ b/ij/plugin/ImageInfo.java
@@ -1,4 +1,4 @@
-package ij.plugin.filter;
+package ij.plugin;
 import ij.*;
 import ij.gui.*;
 import ij.process.*;
@@ -6,31 +6,47 @@ import ij.measure.*;
 import ij.io.*;
 import ij.util.Tools;
 import ij.plugin.frame.Editor;
+import ij.plugin.filter.Analyzer;
 import ij.text.TextWindow;
+import ij.macro.Interpreter;
 import java.awt.*;
 import java.util.*;
 import java.lang.reflect.*;
 
 /** This plugin implements the Image/Show Info command. */
-public class Info implements PlugInFilter {
-    private ImagePlus imp;
+public class ImageInfo implements PlugIn {
 
-	public int setup(String arg, ImagePlus imp) {
-		this.imp = imp;
-		return DOES_ALL+NO_CHANGES;
-	}
-
-	public void run(ImageProcessor ip) {
-		String info = getImageInfo(imp, ip);
-		if (info.indexOf("----")>0)
-			showInfo(info, 450, 500);
+	public void run(String arg) {
+		ImagePlus imp = WindowManager.getCurrentImage();
+		if (imp==null)
+			showInfo();
 		else {
-			int inc = info.contains("No Selection")?0:75;
-			showInfo(info, 300, 350+inc);
+			String info = getImageInfo(imp);
+			if (info.contains("----"))
+				showInfo(imp, info, 450, 500);
+			else {
+				int inc = info.contains("No Selection")?0:75;
+				showInfo(imp, info, 300, 350+inc);
+			}
 		}
 	}
+	
+	private void showInfo() {
+		String s = new String("");
+		if (IJ.getInstance()!=null)
+			s += IJ.getInstance().getInfo()+"\n \n";
+		s += "No images are open\n";
+		Dimension screen = IJ.getScreenSize();
+		s += "ImageJ home: "+IJ.getDir("imagej")+"\n";
+		s += "Java home: "+System.getProperty("java.home")+"\n";
+		s += "Screen size: "+screen.width+"x"+screen.height+"\n";
+		if (IJ.isMacOSX())
+			s += "SetMenuBarCount: "+Menus.setMenuBarCount+"\n";
+		new TextWindow("Info", s, 600, 300);
+	}
 
-	public String getImageInfo(ImagePlus imp, ImageProcessor ip) {
+	public String getImageInfo(ImagePlus imp) {
+		ImageProcessor ip = imp.getProcessor();
 		String infoProperty = null;
 		if (imp.getStackSize()>1) {
 			ImageStack stack = imp.getStack();
@@ -84,7 +100,7 @@ public class Info implements PlugInFilter {
 			return null;
 	}
 
-	String getInfo(ImagePlus imp, ImageProcessor ip) {
+	private String getInfo(ImagePlus imp, ImageProcessor ip) {
 		String s = new String("");
 		if (IJ.getInstance()!=null)
 			s += IJ.getInstance().getInfo()+"\n \n";
@@ -226,9 +242,11 @@ public class Info implements PlugInFilter {
 				s += "  Composite mode: \"" + mode + "\"\n";
 			}
 		}
-
+		
+		if (imp.isLocked())
+			s += "**Locked**\n";
 		if (ip.getMinThreshold()==ImageProcessor.NO_THRESHOLD)
-	    	s += "No threshold\n";
+			s += "No threshold\n";
 	    else {
 	    	double lower = ip.getMinThreshold();
 	    	double upper = ip.getMaxThreshold();
@@ -284,6 +302,8 @@ public class Info implements PlugInFilter {
 			Dimension screen = IJ.getScreenSize();
 			s += "Screen location: "+loc.x+","+loc.y+" ("+screen.width+"x"+screen.height+")\n";
 		}
+		if (IJ.isMacOSX())
+			s += "SetMenuBarCount: "+Menus.setMenuBarCount+"\n";
 		
 		String zOrigin = stackSize>1||cal.zOrigin!=0.0?","+d2s(cal.zOrigin):"";
 		String origin = d2s(cal.xOrigin)+","+d2s(cal.yOrigin)+zOrigin;
@@ -301,6 +321,10 @@ public class Info implements PlugInFilter {
 		} else
 	    	s += "No overlay\n";
 
+		Interpreter interp = Interpreter.getInstance();
+		if (interp!=null)
+			s += "Macro is running"+(Interpreter.isBatchMode()?" in batch mode":"")+"\n";
+
 	    Roi roi = imp.getRoi();
 	    if (roi == null) {
 			if (cal.calibrated())
@@ -374,16 +398,16 @@ public class Info implements PlugInFilter {
 	}
 	
 	// returns a Y coordinate based on the "Invert Y Coodinates" flag
-	int yy(int y, ImagePlus imp) {
+	private int yy(int y, ImagePlus imp) {
 		return Analyzer.updateY(y, imp.getHeight());
 	}
 
 	// returns a Y coordinate based on the "Invert Y Coodinates" flag
-	double yy(double y, ImagePlus imp) {
+	private double yy(double y, ImagePlus imp) {
 		return Analyzer.updateY(y, imp.getHeight());
 	}
 
-	void showInfo(String info, int width, int height) {
+	private void showInfo(ImagePlus imp, String info, int width, int height) {
 		new TextWindow("Info for "+imp.getTitle(), info, width, height);
 		//Editor ed = new Editor();
 		//ed.setSize(width, height);
diff --git a/ij/plugin/ImageJ_Updater.java b/ij/plugin/ImageJ_Updater.java
index b944111..27786ef 100644
--- a/ij/plugin/ImageJ_Updater.java
+++ b/ij/plugin/ImageJ_Updater.java
@@ -10,12 +10,16 @@ import java.lang.reflect.*;
 
 /** This plugin implements the Help/Update ImageJ command. */
 public class ImageJ_Updater implements PlugIn {
+	private static final String URL = "http://wsr.imagej.net";
 	private String notes;
 
 	public void run(String arg) {
-		if (arg.equals("menus"))
-			{updateMenus(); return;}
-		if (IJ.getApplet()!=null) return;
+		if (arg.equals("menus")) {
+			updateMenus();
+			return;
+		}
+		if (IJ.getApplet()!=null)
+			return;
 		URL url = getClass().getResource("/ij/IJ.class");
 		String ij_jar = url == null ? null : url.toString().replaceAll("%20", " ");
 		if (ij_jar==null || !ij_jar.startsWith("jar:file:")) {
@@ -35,41 +39,33 @@ public class ImageJ_Updater implements PlugIn {
 			error(msg);
 			return;
 		}
-		String[] list = openUrlAsList(IJ.URL+"/download/jars/list.txt");
-		int count = list.length + 3;
+		String[] list = openUrlAsList(URL+"/jars/list.txt");
+		int count = list.length + 2;
 		String[] versions = new String[count];
 		String[] urls = new String[count];
-		String uv = getUpgradeVersion();
-		if (uv==null) return;
-		versions[0] = "v"+uv;
-		urls[0] = IJ.URL+"/upgrade/ij.jar";
-		if (versions[0]==null) return;
+		versions[0] = list[0];
+		urls[0] = URL+"/jars/ij.jar";
 		for (int i=1; i<count-2; i++) {
-			String version = list[i-1];
+			String version = list[i];
 			versions[i] = version.substring(0,version.length()-1); // remove letter
-			urls[i] = IJ.URL+"/download/jars/ij"
-				+version.substring(1,2)+version.substring(3,6)+".jar";
+			urls[i] = URL+"/jars/ij"+version.substring(1,2)+version.substring(3,6)+".jar";
 		}
 		versions[count-2] = "daily build";
-		urls[count-2] = IJ.URL+"/ij.jar";
+		urls[count-2] = URL+"/download/daily-build/ij.jar";
 		versions[count-1] = "previous";
-		urls[count-1] = IJ.URL+"/upgrade/ij2.jar";
+		urls[count-1] = URL+"/jars/ij2.jar";
+		//for (int i=0; i<count; i++)
+		//	IJ.log(i+" "+versions[i]+"  "+urls[i]);
 		int choice = showDialog(versions);
 		if (choice==-1 || !Commands.closeAll())
 			return;
-		//System.out.println("choice: "+choice);
-		//for (int i=0; i<urls.length; i++) System.out.println("  "+i+" "+urls[i]);
 		byte[] jar = null;
-		if ("daily build".equals(versions[choice]) && notes!=null && notes.contains(" </title>"))
-			jar = getJar("http://wsr.imagej.net/download/daily-build/ij.jar");
-		if (jar==null)
-			jar = getJar(urls[choice]);
+		jar = getJar(urls[choice]);
 		if (jar==null) {
 			error("Unable to download ij.jar from "+urls[choice]);
 			return;
 		}
 		Prefs.savePreferences();
-		//System.out.println("saveJar: "+file);
 		saveJar(file, jar);
 		if (choice<count-2) // force macro Function Finder to download fresh list
 			new File(IJ.getDirectory("macros")+"functions.html").delete();
@@ -77,7 +73,7 @@ public class ImageJ_Updater implements PlugIn {
 	}
 
 	int showDialog(String[] versions) {
-		GenericDialog gd = new GenericDialog("ImageJ Updater");
+		GenericDialog gd = new GenericDialog("ImageJ Updater 2");
 		gd.addChoice("Upgrade To:", versions, versions[0]);
 		String msg = 
 			"You are currently running v"+ImageJ.VERSION+ImageJ.BUILD+".\n"+
@@ -93,24 +89,6 @@ public class ImageJ_Updater implements PlugIn {
 			return gd.getNextChoiceIndex();
 	}
 
-	String getUpgradeVersion() {
-		String url = IJ.URL+"/notes.html";
-		notes = openUrlAsString(url, 20);
-		if (notes==null) {
-			error("Unable to connect to "+IJ.URL+". You\n"
-				+"may need to use the Edit>Options>Proxy Settings\n"
-				+"command to configure ImageJ to use a proxy server.");
-			return null;
-		}
-		int index = notes.indexOf("Version ");
-		if (index==-1) {
-			error("Release notes are not in the expected format");
-			return null;
-		}
-		String version = notes.substring(index+8, index+13);
-		return version;
-	}
-
 	String openUrlAsString(String address, int maxLines) {
 		StringBuffer sb;
 		try {
@@ -128,7 +106,6 @@ public class ImageJ_Updater implements PlugIn {
 	}
 
 	byte[] getJar(String address) {
-		//System.out.println("getJar: "+address);
 		byte[] data;
 		try {
 			URL url = new URL(address);
@@ -138,7 +115,7 @@ public class ImageJ_Updater implements PlugIn {
 			if (IJ.debugMode) IJ.log("Updater (url): "+ address + " "+ len);
 			if (len<=0)
 				return null;
-			String name = address.contains("wsr")?"daily build (":"ij.jar (";
+			String name = address.contains("daily")?"daily build (":"ij.jar (";
 			IJ.showStatus("Downloading "+ name + IJ.d2s((double)len/1048576,1)+"MB)");
 			InputStream in = uc.getInputStream();
 			data = new byte[len];
@@ -159,23 +136,6 @@ public class ImageJ_Updater implements PlugIn {
 		return data;
 	}
 
-	/*Changes the name of ij.jar to ij-old.jar
-	boolean renameJar(File f) {
-		File backup = new File(Prefs.getImageJDir() + "ij-old.jar");
-		if (backup.exists()) {
-			if (!backup.delete()) {
-				error("Unable to delete backup: "+backup.getPath());
-				return false;
-			}
-		}
-		if (!f.renameTo(backup)) {
-			error("Unable to rename to ij-old.jar: "+f.getPath());
-			return false;
-		}
-		return true;
-	}
-	*/
-
 	void saveJar(File f, byte[] data) {
 		try {
 			FileOutputStream out = new FileOutputStream(f);
@@ -206,25 +166,6 @@ public class ImageJ_Updater implements PlugIn {
 		return lines;
 	}
 
-	// Use reflection to get version since early versions
-	// of ImageJ do not have the IJ.getVersion() method.
-	/*
-	String version() {
-		String version = "";
-		try {
-			Class ijClass = ImageJ.class;
-			Field field = ijClass.getField("VERSION");
-			version = (String)field.get(ijClass);
-		} catch (Exception ex) {}
-		return version;
-	}
-	*/
-
-	boolean isMac() {
-		String osname = System.getProperty("os.name");
-		return osname.startsWith("Mac");
-	}
-	
 	void error(String msg) {
 		IJ.error("ImageJ Updater", msg);
 	}
diff --git a/ij/plugin/ImagesToStack.java b/ij/plugin/ImagesToStack.java
index 44eac5d..9d82bba 100644
--- a/ij/plugin/ImagesToStack.java
+++ b/ij/plugin/ImagesToStack.java
@@ -117,6 +117,7 @@ public class ImagesToStack implements PlugIn {
 		ImageStack stack = new ImageStack(width, height);
 		FileInfo fi = image[0].getOriginalFileInfo();
 		if (fi!=null && fi.directory==null) fi = null;
+		Overlay overlay = new Overlay();
 		for (int i=0; i<count; i++) {
 			ImageProcessor ip = image[i].getProcessor();
 			boolean invertedLut = ip.isInvertedLut();
@@ -168,8 +169,18 @@ public class ImagesToStack implements PlugIn {
 						ip = ip.resize(width, height);
 						break;
 				}
-			} else if (keep)
-				ip = ip.duplicate();
+			} else {
+				if (keep)
+					ip = ip.duplicate();
+				Overlay overlay2 = image[i].getOverlay();
+				if (overlay2!=null) {
+					for (int j=0; j<overlay2.size(); j++) {
+						Roi roi = overlay2.get(j);
+						roi.setPosition(i+1);
+						overlay.add((Roi)roi.clone());
+					}
+				}
+			}
 			stack.addSlice(label, ip);
 			if (i==0 && invertedLut && !allInvertedLuts)
 				stack.setColorModel(null);
@@ -189,6 +200,8 @@ public class ImagesToStack implements PlugIn {
 			fi.nImages = imp.getStackSize();
 			imp.setFileInfo(fi);
 		}
+		if (overlay.size()>0)
+			imp.setOverlay(overlay);
 		imp.show();
 	}
 	
diff --git a/ij/plugin/JavaScriptEvaluator.java b/ij/plugin/JavaScriptEvaluator.java
index a4ee3c1..21762b8 100644
--- a/ij/plugin/JavaScriptEvaluator.java
+++ b/ij/plugin/JavaScriptEvaluator.java
@@ -39,22 +39,28 @@ public class JavaScriptEvaluator implements PlugIn, Runnable  {
 		try {
 			ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
 			ScriptEngine engine = scriptEngineManager.getEngineByName("ECMAScript");
-			if (engine == null)
-				{IJ.error("Could not find JavaScript engine"); return;}
-			engine.eval("function load(path) {\n"
-				+ "  importClass(Packages.sun.org.mozilla.javascript.internal.Context);\n"
-				+ "  importClass(Packages.java.io.FileReader);\n"
-				+ "  var cx = Context.getCurrentContext();\n"
-				+ "  cx.evaluateReader(this, new FileReader(path), path, 1, null);\n"
-				+ "}");
+			if (engine == null) {
+				IJ.error("Could not find JavaScript engine");
+				return;
+			}
+			if (!IJ.isJava18()) {
+				engine.eval("function load(path) {\n"
+					+ "  importClass(Packages.sun.org.mozilla.javascript.internal.Context);\n"
+					+ "  importClass(Packages.java.io.FileReader);\n"
+					+ "  var cx = Context.getCurrentContext();\n"
+					+ "  cx.evaluateReader(this, new FileReader(path), path, 1, null);\n"
+					+ "}");
+			}
 			result = engine.eval(script);
 		} catch(Throwable e) {
 			String msg = e.getMessage();
+			if (msg==null)
+				msg = "";
 			if (msg.startsWith("sun.org.mozilla.javascript.internal.EcmaError: "))
 				msg = msg.substring(47, msg.length());
 			if (msg.startsWith("sun.org.mozilla.javascript.internal.EvaluatorException"))
 				msg = "Error"+msg.substring(54, msg.length());
-			if (!msg.contains("Macro canceled"))
+			if (msg.length()>0 && !msg.contains("Macro canceled"))
 				IJ.log(msg);
 		}
 	}
diff --git a/ij/plugin/Macro_Runner.java b/ij/plugin/Macro_Runner.java
index bffc236..899ba0e 100644
--- a/ij/plugin/Macro_Runner.java
+++ b/ij/plugin/Macro_Runner.java
@@ -12,6 +12,7 @@ import java.lang.reflect.*;
 /** This class runs macros and scripts installed in the Plugins menu as well as
 	macros and scripts opened using the Plugins/Macros/Run command. */
 public class Macro_Runner implements PlugIn {
+	private static String filePath;
 	
 	/** Opens and runs the specified macro file (.txt or .ijm) or script file (.js, .bsh or .py)  
 		on the current thread. Displays a file open dialog if <code>name</code> 
@@ -120,6 +121,7 @@ public class Macro_Runner implements PlugIn {
             IJ.error("RunMacro", "Macro or script not found:\n \n"+path);
 			return null;
 		}
+		filePath = path;
 		try {
 			int size = (int)f.length();
 			byte[] buffer = new byte[size];
@@ -234,9 +236,8 @@ public class Macro_Runner implements PlugIn {
 		as a string, the last expression evaluated by the script. */
 	public String runJavaScript(String script, String arg) {
 		Object js = null;
-		if ((IJ.isJava16() && !(IJ.isMacOSX()&&!IJ.is64Bit())) && !IJ.isJava18()) {
-			// Use JavaScript engine built into Java 6 and Java 7.
-			// Can't use incompatible Nashorn engine in Java 8 and later.
+		if ((IJ.isJava16() && !(IJ.isMacOSX()&&!IJ.is64Bit()))) {
+			// Use JavaScript engine built into Java 6 and later.
 			js = IJ.runPlugIn("ij.plugin.JavaScriptEvaluator", "");
 		} else {
 			js = IJ.runPlugIn("JavaScript", "");
@@ -247,6 +248,8 @@ public class Macro_Runner implements PlugIn {
 			}
 		}
 		script = Editor.getJSPrefix(arg)+script;
+		if (IJ.isJava18())
+			script = "load(\"nashorn:mozilla_compat.js\");" + script;
 		if (js!=null)
 			return runScript(js, script, arg);
 		else
@@ -294,7 +297,7 @@ public class Macro_Runner implements PlugIn {
 			return null;
 	}
 	
-	/** Runs a Prython script on the current thread, passing a string argument, 
+	/** Runs a Python script on the current thread, passing a string argument, 
 		which the script can retrieve using the getArgument() function. Returns,
 		as a string, the value of the variable 'result'. For example, a Python script  
 		containing the line "result=123" will return the string "123".
@@ -330,5 +333,14 @@ public class Macro_Runner implements PlugIn {
 		}
 		return ok;
 	}
+	
+	/** Returns the file path of the most recently loaded macro or script. */
+	public static String getFilePath() {
+		return filePath;
+	}
+
+	public static void setFilePath(String path) {
+		filePath = path;
+	}
 
 }
diff --git a/ij/plugin/MontageMaker.java b/ij/plugin/MontageMaker.java
index a06bcf3..ab1e81f 100644
--- a/ij/plugin/MontageMaker.java
+++ b/ij/plugin/MontageMaker.java
@@ -47,7 +47,9 @@ public class MontageMaker implements PlugIn {
 			if (ci.getMode()!=mode)
 				ci.setMode(mode);
 			imp.setPosition(channel, imp.getSlice(), imp.getFrame());
+			Calibration cal = imp.getCalibration();
 			imp = new ImagePlus(imp.getTitle(), stack);
+			imp.setCalibration(cal);
 		}
 		makeMontage(imp);
 		imp.updateImage();
@@ -235,6 +237,7 @@ public class MontageMaker implements PlugIn {
 			montages[i] = makeMontage2(channels[i], columns, rows, scale, 1, last, inc, borderWidth, labels);
 		}
 		ImagePlus montage = (new RGBStackMerge()).mergeHyperstacks(montages, false);
+		montage.setCalibration(montages[0].getCalibration());
 		montage.setTitle("Montage");
 		return montage;
 	}
diff --git a/ij/plugin/Orthogonal_Views.java b/ij/plugin/Orthogonal_Views.java
index 0476e55..83a9ed8 100644
--- a/ij/plugin/Orthogonal_Views.java
+++ b/ij/plugin/Orthogonal_Views.java
@@ -51,21 +51,31 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 	private static int previousID, previousX, previousY;
 	private Rectangle startingSrcRect;
 	private boolean done;
+	private boolean initialized;
+	private boolean sliceSet;
 	private Thread thread;
 
 	 
 	public void run(String arg) {
 		imp = IJ.getImage();
+		boolean isStack = imp.getStackSize()>1;
+		hyperstack = imp.isHyperStack();
+		if ((hyperstack||imp.isComposite()) && imp.getNSlices()<=1)
+			isStack = false;
 		if (instance!=null) {
-			instance.dispose();
-			return;
-		}
-		if (imp.getStackSize()==1) {
-			IJ.error("Othogonal Views", "This command requires a stack.");
-			return;
+			if (imp==instance.imp) {
+				instance.dispose();
+				return;
+			} else if (isStack) {
+				instance.dispose();
+				if (IJ.isMacro()) IJ.wait(1000);
+			} else {
+				ImageWindow win = instance.imp!=null?instance.imp.getWindow():null;
+				if (win!=null) win.toFront();
+				return;
+			}
 		}
-		hyperstack = imp.isHyperStack();
-		if ((hyperstack||imp.isComposite()) && imp.getNSlices()<=1) {
+		if (!isStack) {
 			IJ.error("Othogonal Views", "This command requires a stack, or a hypertack with Z>1.");
 			return;
 		}
@@ -75,11 +85,11 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 		if (yz_image==null || yz_image.getHeight()!=imp.getHeight() || yz_image.getBitDepth()!=yzBitDepth)
 			yz_image = new ImagePlus();
 		xz_image = WindowManager.getImage(xzID);
-		//if (xz_image!=null) IJ.log(imp+"  "+xz_image+"  "+xz_image.getHeight()+"  "+imp.getHeight()+"  "+xz_image.getBitDepth()+"  "+yzBitDepth);
 		if (xz_image==null || xz_image.getWidth()!=imp.getWidth() || xz_image.getBitDepth()!=yzBitDepth)
 			xz_image = new ImagePlus();
 		instance = this;
-		ImageProcessor ip = hyperstack?new ColorProcessor(imp.getImage()):imp.getProcessor();
+		int mode = imp.getCompositeMode();
+		ImageProcessor ip = mode==IJ.COMPOSITE?new ColorProcessor(imp.getImage()):imp.getProcessor();
 		min = ip.getMin();
 		max = ip.getMax();
 		cal=this.imp.getCalibration();
@@ -118,18 +128,34 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 	private ImageStack getStack() {
 		if (imp.isHyperStack()) {
 			int slices = imp.getNSlices();
-			ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());
-			int c=imp.getChannel(), z=imp.getSlice(), t=imp.getFrame();
+			int c=imp.getChannel();
+			int z=imp.getSlice();
+			int t=imp.getFrame();
+			int mode = imp.getCompositeMode();
+			rgb = mode==IJ.COMPOSITE;
+			ColorModel cm = rgb?null:imp.getProcessor().getColorModel();
+			//IJ.log("getStack; "+c+" "+currentChannel+" "+fp1);
+			if (cm!=null && fp1!=null && fp1.getBitDepth()!=24) {
+				fp1.setColorModel(cm);
+				fp2.setColorModel(cm);
+			}
+			ImageStack stack = imp.getStack();
+			ImageStack stack2 = new ImageStack(imp.getWidth(), imp.getHeight());
 			for (int i=1; i<=slices; i++) {
-				imp.setPositionWithoutUpdate(c, i, t);
-				stack.addSlice(null, new ColorProcessor(imp.getImage()));
+				if (rgb) {
+					imp.setPositionWithoutUpdate(c, i, t);
+					stack2.addSlice(null, new ColorProcessor(imp.getImage()));
+				} else {
+					int index = imp.getStackIndex(c, i, t);
+					stack2.addSlice(null, stack.getProcessor(index));
+				}
 			}
-			imp.setPosition(c, z, t);
+			if (rgb)
+				imp.setPosition(c, z, t);
 			currentChannel = c;
 			currentFrame = t;
-			if (imp.isComposite())
-				currentMode = ((CompositeImage)imp).getMode();
-			return stack;
+			currentMode = mode;
+			return stack2;
 		} else
 			return imp.getStack();
 	}
@@ -159,10 +185,12 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 			cal_yz.pixelHeight=o_height;
 		}
 		yz_image.setCalibration(cal_yz);
+		yz_image.setIJMenuBar(false);
 		cal_xz.setUnit(unit);
 		cal_xz.pixelWidth=o_width;
 		cal_xz.pixelHeight=o_depth/az;
 		xz_image.setCalibration(cal_xz);
+		xz_image.setIJMenuBar(false);
 	}
 
 	private void updateMagnification(int x, int y) {
@@ -189,7 +217,6 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
         double yz_mag = yz_ic.getMagnification();
 		zcoord = (int)(arat*z);
         while (yz_mag<magnification) {
-        	//IJ.log(magnification+"  "+yz_mag+"  "+zcoord+"  "+y+"  "+x);
         	yz_ic.zoomIn(yz_ic.screenX(zcoord), yz_ic.screenY(y));
         	yz_mag = yz_ic.getMagnification();
         }
@@ -229,7 +256,6 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 			height2 = (int)Math.round(fp2.getHeight()*az);
 			title = "ZY ";
 		}
-		//IJ.log("updateViews "+width2+" "+height2+" "+arat+" "+ay+" "+fp2);
 		if (width2!=fp2.getWidth()||height2!=fp2.getHeight()) {
 			fp2.setInterpolate(true);
 			ImageProcessor sfp2=fp2.resize(width2, height2);
@@ -245,14 +271,14 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 			yz_image.show();
 			ImageCanvas ic = yz_image.getCanvas();
 			ic.addKeyListener(this);
-			//ic.addMouseListener(this);
+			ic.addMouseListener(this);
 			//ic.addMouseMotionListener(this);
 			ic.setCustomRoi(true);
 			//yz_image.getWindow().addMouseWheelListener(this);
 			yzID = yz_image.getID();
 		} else {
 			ImageCanvas ic = yz_image.getWindow().getCanvas();
-			//ic.addMouseListener(this);
+			ic.addMouseListener(this);
 			//ic.addMouseMotionListener(this);
 			ic.setCustomRoi(true);
 		}
@@ -260,14 +286,14 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 			xz_image.show();
 			ImageCanvas ic = xz_image.getCanvas();
 			ic.addKeyListener(this);
-			//ic.addMouseListener(this);
+			ic.addMouseListener(this);
 			//ic.addMouseMotionListener(this);
 			ic.setCustomRoi(true);
 			//xz_image.getWindow().addMouseWheelListener(this);
 			xzID = xz_image.getID();
 		} else {
 			ImageCanvas ic = xz_image.getWindow().getCanvas();
-			//ic.addMouseListener(this);
+			ic.addMouseListener(this);
 			//ic.addMouseMotionListener(this);
 			ic.setCustomRoi(true);
 		}
@@ -299,10 +325,12 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
  				xzWin.setLocation(xyX,xyY+xyWin.getHeight());
  			if (firstTime) {
  				imp.getWindow().toFront();
- 				if (hyperstack)
- 					imp.setPosition(imp.getChannel(), imp.getNSlices()/2, imp.getFrame());
- 				else
- 					imp.setSlice(imp.getNSlices()/2);
+ 				if (!sliceSet) {
+					if (hyperstack)
+						imp.setPosition(imp.getChannel(), imp.getNSlices()/2, imp.getFrame());
+					else
+						imp.setSlice(imp.getNSlices()/2);
+ 				}
  				firstTime = false;
  			}
 		}
@@ -321,7 +349,6 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 		double brat=1.0;//az/ay;
 		int za=(int)(ds*arat);
 		int zb=(int)(ds*brat);
-		//IJ.log("za: "+za +" zb: "+zb);
 		
 		if (ip instanceof FloatProcessor) {
 			fp1=new FloatProcessor(width,za);
@@ -347,7 +374,6 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 				fp2=new ShortProcessor(height,zb);
 			else
 				fp2=new ShortProcessor(zb,height);
-			//IJ.log("createProcessors "+rotateYZ+"  "+height+"   "+zb+"  "+fp2);
 			return true;
 		}
 		
@@ -534,7 +560,7 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 		path.moveTo(x, 0f);
 		path.lineTo(x, height);	
 	}
-	      
+	
 	void dispose() {
 		synchronized(this) {
 			done = true;
@@ -557,6 +583,8 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 				ic.setCustomRoi(false);
 			}
 		}
+		xz_image.changes = false;
+		xz_image.close();
 		yz_image.setOverlay(null);
 		ImageWindow win2 = yz_image.getWindow();
 		if (win2!=null) {
@@ -569,6 +597,8 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 				ic.setCustomRoi(false);
 			}
 		}
+		yz_image.changes = false;
+		yz_image.close();
 		ImagePlus.removeImageListener(this);
 		Executer.removeCommandListener(this);
 		win.removeWindowListener(this);
@@ -669,21 +699,25 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 	}
 	
 	private void exec() {
-		if (canvas==null) return;
+		if (canvas==null)
+			return;
 		int width=imp.getWidth();
 		int height=imp.getHeight();
 		if (hyperstack) {
-			int c = imp.getChannel();
-			int t = imp.getFrame();
-			if (c!=currentChannel || t!=currentFrame)
-				imageStack = null;
+			int mode = IJ.COMPOSITE;
 			if (imp.isComposite()) {
-				int mode = ((CompositeImage)imp).getMode();
+				mode = ((CompositeImage)imp).getMode();
 				if (mode!=currentMode)
 					imageStack = null;
 			}
+			if (imageStack!=null) {
+				int c = imp.getChannel();
+				int t = imp.getFrame();
+				if ((mode!=IJ.COMPOSITE&&c!=currentChannel) || t!=currentFrame)
+					imageStack = null;
+			}
 		}
-		ImageStack is=imageStack;
+		ImageStack is = imageStack;
 		if (is==null)
 			is = imageStack = getStack();
 		double arat=az/ax;
@@ -696,11 +730,13 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 		updateViews(p, is);
 		GeneralPath path = new GeneralPath();
 		drawCross(imp, p, path);
-		imp.setOverlay(path, color, new BasicStroke(1));
+		if (!done)
+			imp.setOverlay(path, color, new BasicStroke(1));
 		canvas.setCustomRoi(true);
 		updateCrosses(p.x, p.y, arat, brat);
 		if (syncZoom) updateMagnification(p.x, p.y);
 		arrangeWindows(sticky);
+		initialized = true;
 	}
 
 	private void updateCrosses(int x, int y, double arat, double brat) {
@@ -714,7 +750,8 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 		p=new Point (x, zcoord);
 		GeneralPath path = new GeneralPath();
 		drawCross(xz_image, p, path);
-		xz_image.setOverlay(path, color, new BasicStroke(1));
+		if (!done)
+			xz_image.setOverlay(path, color, new BasicStroke(1));
 		if (rotateYZ) {
 			if (flipXZ)
 				zcoord=(int)Math.round(brat*(z-zlice));
@@ -727,7 +764,8 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 		}
 		path = new GeneralPath();
 		drawCross(yz_image, p, path);
-		yz_image.setOverlay(path, color, new BasicStroke(1));
+		if (!done)
+			yz_image.setOverlay(path, color, new BasicStroke(1));
 		IJ.showStatus(imp.getLocationAsString(crossLoc.x, crossLoc.y));
 	}
 
@@ -762,7 +800,8 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 	}
 
 	public void imageClosed(ImagePlus imp) {
-		dispose();
+		if (!done)
+			dispose();
 	}
 
 	public void imageOpened(ImagePlus imp) {
@@ -820,7 +859,8 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 	}
 
 	public void windowClosing(WindowEvent e) {
-		dispose();		
+		if (!done)
+			dispose();		
 	}
 
 	public void windowDeactivated(WindowEvent e) {
@@ -866,6 +906,16 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 			return null;
 	}
 	
+	public static int getImageID() {
+		ImagePlus img = getImage();
+		return img!=null?img.getID():0;
+	}
+
+ 	public static void stop() {
+		if (instance!=null)
+			instance.dispose();
+	}
+
 	public static synchronized boolean isOrthoViewsImage(ImagePlus imp) {
 		if (imp==null || instance==null)
 			return false;
@@ -887,10 +937,17 @@ public class Orthogonal_Views implements PlugIn, MouseListener, MouseMotionListe
 	
 	public void setCrossLoc(int x, int y, int z) {
 		crossLoc.setLocation(x, y);
-		if (hyperstack)
-			imp.setPosition(imp.getChannel(), z+1, imp.getFrame());
-		else
-			imp.setSlice(z+1);
+		int slice = z+1;
+		if (slice!=imp.getSlice()) {
+			if (hyperstack)
+				imp.setPosition(imp.getChannel(), slice, imp.getFrame());
+			else
+				imp.setSlice(slice);
+			sliceSet = true;
+		}
+		while (!initialized) {
+			IJ.wait(10);
+		}
 		update();
 	}
 	
diff --git a/ij/plugin/PluginInstaller.java b/ij/plugin/PluginInstaller.java
index d5fe819..eaa9a85 100644
--- a/ij/plugin/PluginInstaller.java
+++ b/ij/plugin/PluginInstaller.java
@@ -30,6 +30,8 @@ public class PluginInstaller implements PlugIn {
 	public boolean install(String path) {
 		boolean isURL = path.contains("://");
 		String lcPath = path.toLowerCase();
+		if (isURL)
+			path = Opener.updateUrl(path);
 		boolean isTool = lcPath.endsWith("tool.ijm") || lcPath.endsWith("tool.txt")
 			|| lcPath.endsWith("tool.class") || lcPath.endsWith("tool.jar");
 		boolean isMacro = lcPath.endsWith(".txt") || lcPath.endsWith(".ijm");
@@ -155,7 +157,7 @@ public class PluginInstaller implements PlugIn {
 		int n = 0;
 		try {
 			url = new URL(urlString);
-			if (IJ.debugMode) IJ.log("Downloading: "+urlString+"  " +url);
+			if (IJ.debugMode) IJ.log("PluginInstaller: "+urlString+"  " +url);
 			if (url==null)
 				return null;
 			URLConnection uc = url.openConnection();
diff --git a/ij/plugin/PointToolOptions.java b/ij/plugin/PointToolOptions.java
index a60e538..ca6b071 100644
--- a/ij/plugin/PointToolOptions.java
+++ b/ij/plugin/PointToolOptions.java
@@ -20,12 +20,13 @@ public class PointToolOptions implements PlugIn, DialogListener {
 	+"<ul>"
 	+"<li> Alt-click, or control-click, on a point to delete it.<br>"
 	+"<li> Press 'y' (<i>Edit>Selection>Properties</i>) to display<br>the counts in a results table.<br>"
+	+"<li> Press 'm' (<i>Analyze>Measure</i>) to display the<br>point stack positions in the results table.<br>"
 	+"<li> Use <i>File>Save As>Tiff</i> or <i>File>Save As>Selection</i><br>to save the points and counts.<br>"
+	+"<li> Hold the shift key down and points will be<br>constrained to a horizontal or vertical line.<br>"
 	+"</ul>"
 	+" <br>"
 	+"</font>";
-
-
+ 
  	public void run(String arg) {
  		if (gd!=null && gd.isShowing()) {
  			gd.toFront();
@@ -65,6 +66,7 @@ public class PointToolOptions implements PlugIn, DialogListener {
 		gd.setInsets(5, 20, 0);
 		gd.addCheckbox("Label points", !Prefs.noPointLabels);
 		if (multipointTool) {
+			gd.addCheckbox("Show all", Prefs.showAllPoints);
 			gd.setInsets(15,0,5);
 			String[] choices =  PointRoi.getCounterChoices();
 			gd.addChoice("Counter:", choices, choices[getCounter()]);
@@ -115,6 +117,10 @@ public class PointToolOptions implements PlugIn, DialogListener {
 			redraw = true;
 		Prefs.noPointLabels = noPointLabels;
 		if (multipointTool) {
+			boolean showAllPoints = gd.getNextBoolean();
+			if (showAllPoints!=Prefs.showAllPoints)
+				redraw = true;
+			Prefs.showAllPoints = showAllPoints;
 			int counter = gd.getNextChoiceIndex();
 			if (counter!=getCounter()) {
 				setCounter(counter);
@@ -122,57 +128,56 @@ public class PointToolOptions implements PlugIn, DialogListener {
 			}
 		}
 		if (redraw) {
-			ImagePlus imp = WindowManager.getCurrentImage();
-			if (imp!=null) {
-				Roi roi = imp.getRoi();
-				if (roi instanceof PointRoi)
-					((PointRoi)roi).setShowLabels(!Prefs.noPointLabels);
-				imp.draw();
+     		PointRoi roi = getPointRoi();
+     		if (roi!=null) {
+				roi.setShowLabels(!Prefs.noPointLabels);
+				ImagePlus imp = roi.getImage();
+				if (imp!=null) imp.draw();
 			}
 		}
 		return true;
     }
     
     private static int getCounter() {
-    	int counter = 0;
-		ImagePlus imp = WindowManager.getCurrentImage();
-		if (imp!=null) {
-			Roi roi = imp.getRoi();
-			if (roi instanceof PointRoi)
-				counter = ((PointRoi)roi).getCounter();
-		}
-		return counter;
+     	PointRoi roi = getPointRoi();
+     	return roi!=null?roi.getCounter():0;
     }
     
     private static void setCounter(int counter) {
-		ImagePlus imp = WindowManager.getCurrentImage();
-		if (imp!=null) {
-			Roi roi = imp.getRoi();
-			if (roi instanceof PointRoi)
-				((PointRoi)roi).setCounter(counter);
-		}
+    	PointRoi roi = getPointRoi();
+		if (roi!=null)
+			roi.setCounter(counter);
+		PointRoi.setDefaultCounter(counter);
+    }
+    
+    private static PointRoi getPointRoi() {
+    	ImagePlus imp = WindowManager.getCurrentImage();
+    	if (imp==null)
+    		return null;
+		Roi roi = imp.getRoi();
+		if (roi==null)
+			return null;
+		if (roi instanceof PointRoi)
+			return (PointRoi)roi;
+		else
+			return null;
     }
 
     private static int getCount(int counter) {
-    	int count = 0;
-    	ImagePlus imp = WindowManager.getCurrentImage();
-    	if (imp!=null) {
-    		Roi roi = imp.getRoi();
-    		if (roi==null)
-    			return 0;
-    		if (roi!=null && (roi instanceof PointRoi))
-				count = ((PointRoi)roi).getCount(counter);
-    	}
-    	return count;
+     	PointRoi roi = getPointRoi();
+     	return roi!=null?roi.getCount(counter):0;
     }
     
     public static void update() {
     	if (gd!=null && gd.isShowing()) {
 			Vector choices = gd.getChoices();
-			Choice choice = (Choice)choices.elementAt(3);
+			if (choices==null || choices.size()<4)
+				return;
+			Choice counterChoice = (Choice)choices.elementAt(3);
 			int counter = getCounter();
-			choice.select(counter);
-			((Label)gd.getMessage()).setText(""+getCount(counter));
+			int count = getCount(counter);
+			counterChoice.select(counter);
+			((Label)gd.getMessage()).setText(""+count);
 		}
     }
     			
diff --git a/ij/plugin/Profiler.java b/ij/plugin/Profiler.java
index f490674..fe5d150 100644
--- a/ij/plugin/Profiler.java
+++ b/ij/plugin/Profiler.java
@@ -46,22 +46,23 @@ public Plot getPlot() {
 		boolean fixedScale = ymin!=0.0 || ymax!=0.0;
 		boolean wasFixedScale = fixedScale;
 		
-		GenericDialog gd = new GenericDialog("Profile Plot Options", IJ.getInstance());
-		gd.addNumericField("Width (pixels):", PlotWindow.plotWidth, 0);
-		gd.addNumericField("Height (pixels):", PlotWindow.plotHeight, 0);
+		GenericDialog gd = new GenericDialog("Plot Options", IJ.getInstance());
+		gd.addNumericField("Width:", PlotWindow.plotWidth, 0);
+		gd.addNumericField("Height:", PlotWindow.plotHeight, 0);
 		gd.addNumericField("Font Size:", PlotWindow.fontSize, 0);
-		gd.setInsets(10,20,0); //distance to previous
-		gd.addCheckbox("Fixed y-axis scale", fixedScale);
-		gd.addNumericField("Minimum Y:", ymin, 2);
-		gd.addNumericField("Maximum Y:", ymax, 2);
 		gd.setInsets(5,20,0); //distance to previous
 		gd.addCheckbox("Draw grid lines", !PlotWindow.noGridLines);
 		gd.addCheckbox("Draw_ticks", !PlotWindow.noTicks);
-		gd.setInsets(10,20,0); //distance to previous
-		gd.addCheckbox("Do not save x-values", !PlotWindow.saveXValues);
 		gd.addCheckbox("Auto-close", PlotWindow.autoClose);
 		gd.addCheckbox("List values", PlotWindow.listValues);
-		gd.setInsets(10,20,0); //distance to previous
+		
+		gd.setInsets(15,0,0);
+		gd.addMessage("--------Profile Plot Options--------");
+		gd.setInsets(5,20,0);
+		gd.addCheckbox("Fixed y-axis scale", fixedScale);
+		gd.addNumericField("Minimum Y:", ymin, 2);
+		gd.addNumericField("Maximum Y:", ymax, 2);
+		gd.setInsets(10,20,0);
 		gd.addCheckbox("Vertical profile", Prefs.verticalProfile);
 		gd.addCheckbox("Interpolate line profiles", PlotWindow.interpolate);
 		gd.addCheckbox("Sub-pixel resolution", Prefs.subPixelResolution);
@@ -82,15 +83,15 @@ public Plot getPlot() {
 		if (fontSize > 28) fontSize = 28;
 		if (!gd.invalidNumber())
 			PlotWindow.fontSize = fontSize;
-		fixedScale = gd.getNextBoolean();
-		ymin = gd.getNextNumber();
-		ymax = gd.getNextNumber();
 		PlotWindow.noGridLines = !gd.getNextBoolean();
 		PlotWindow.noTicks = !gd.getNextBoolean();
 		//data options
-		PlotWindow.saveXValues = !gd.getNextBoolean();
 		PlotWindow.autoClose = gd.getNextBoolean();
 		PlotWindow.listValues = gd.getNextBoolean();
+		//profile plot options
+		fixedScale = gd.getNextBoolean();
+		ymin = gd.getNextNumber();
+		ymax = gd.getNextNumber();
 		//profile options
 		Prefs.verticalProfile = gd.getNextBoolean();
 		PlotWindow.interpolate = gd.getNextBoolean();
diff --git a/ij/plugin/RGBStackMerge.java b/ij/plugin/RGBStackMerge.java
index aebcd7d..1857f71 100644
--- a/ij/plugin/RGBStackMerge.java
+++ b/ij/plugin/RGBStackMerge.java
@@ -230,8 +230,11 @@ public class RGBStackMerge implements PlugIn {
 		for (int i=titles.length-1; i>=0; i--) {
 			if (titles!=null && titles[i].startsWith(str) && (firstChannelName==null||titles[i].contains(firstChannelName))) {
 				name = titles[i];
-	 			if (channel==1)
+	 			if (channel==1) {
+	 				if (name==null || name.length()<3)
+	 					return none;
 	 				firstChannelName = name.substring(3);
+	 			}
 				break;
 			}
 		}
diff --git a/ij/plugin/Resizer.java b/ij/plugin/Resizer.java
index 5c9278e..ce2cfe2 100644
--- a/ij/plugin/Resizer.java
+++ b/ij/plugin/Resizer.java
@@ -30,8 +30,10 @@ public class Resizer implements PlugIn, TextListener, ItemListener  {
 			IJ.error(crop?"Crop":"Resize", "Area selection required");
 			return;
 		}
-		if (!imp.lock())
+		if (!imp.lock()) {
+			IJ.log("<<Resizer: image is locked ("+imp+")>>");
 			return;
+		}
 		Rectangle r = ip.getRoi();
 		origWidth = r.width;;
 		origHeight = r.height;
@@ -117,7 +119,6 @@ public class Resizer implements PlugIn, TextListener, ItemListener  {
 				sizeToHeight = true;
 			if (newWidth<=0.0 && !constrain)  newWidth = 50;
 			if (newHeight<=0.0) newHeight = 50;
-			imp.setOverlay(null);
 		}
 		
 		if (!crop && constrain) {
@@ -154,6 +155,28 @@ public class Resizer implements PlugIn, TextListener, ItemListener  {
 							Overlay overlay2 = overlay.crop(roi.getBounds());
 							imp.setOverlay(overlay2);
 						}
+					} else {
+						Overlay overlay = imp.getOverlay();
+						Overlay overlay2 = new Overlay();
+						if (overlay!=null && !imp.getHideOverlay()) {
+							for (int i=0; i<overlay.size(); i++) {
+								Roi roi2 = overlay.get(i);
+								Rectangle bounds = roi2.getBounds();
+								if (roi2 instanceof ImageRoi && bounds.x==0 && bounds.y==0) {
+									ImageRoi iroi = (ImageRoi)roi2;
+									ImageProcessor ip2 = iroi.getProcessor();
+									ip2.setInterpolationMethod(interpolationMethod);
+									ip2 = ip2.resize(newWidth, newHeight, averageWhenDownsizing);
+									iroi.setProcessor(ip2);
+									overlay2.add(iroi);
+								}
+							}
+							if (overlay2.size()>0)
+								imp.setOverlay(overlay2);
+							else
+								imp.setOverlay(null);
+						} else
+							imp.setOverlay(null);
 					}
 					if (restoreRoi && roi!=null) {
 						roi.setLocation(0, 0);
diff --git a/ij/plugin/ScaleBar.java b/ij/plugin/ScaleBar.java
index d0e7642..051d3fc 100644
--- a/ij/plugin/ScaleBar.java
+++ b/ij/plugin/ScaleBar.java
@@ -26,7 +26,7 @@ public class ScaleBar implements PlugIn {
 	static String bcolor = bcolors[0];
 	static boolean boldText = true;
 	static boolean hideText;
-	static boolean createOverlay = true;
+	static boolean createOverlay;
 	static int defaultFontSize = 14;
 	static int fontSize;
 	static boolean labelAll;
@@ -107,7 +107,7 @@ public class ScaleBar implements PlugIn {
 			boldText = hideText = serifFont = createOverlay = false;
 		else
 			updateScalebar();
-		GenericDialog gd = new BarDialog("ScaleBar Plus");
+		GenericDialog gd = new BarDialog("Scale Bar");
 		gd.addNumericField("Width in "+units+": ", barWidth, digits);
 		gd.addNumericField("Height in pixels: ", barHeightInPixels, 0);
 		gd.addNumericField("Font size: ", fontSize, 0);
diff --git a/ij/plugin/Scaler.java b/ij/plugin/Scaler.java
index 2efa56f..31c87f4 100644
--- a/ij/plugin/Scaler.java
+++ b/ij/plugin/Scaler.java
@@ -55,8 +55,16 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 		try {
 			if (newWindow && imp.getStackSize()>1 && processStack)
 				createNewStack(imp, ip);
-			else
-				scale(ip);
+			else {
+				Overlay overlay = imp.getOverlay();
+				if (imp.getHideOverlay())
+					overlay = null;
+				if (overlay!=null && overlay.size()!=1)
+					overlay = null;
+				if (overlay!=null)
+					overlay = overlay.duplicate();
+				scale(ip, overlay);
+			}
 		}
 		catch(OutOfMemoryError o) {
 			IJ.outOfMemory("Scale");
@@ -76,6 +84,12 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 		int method = interpolationMethod;
 		if (w==1 || h==1)
 			method = ImageProcessor.NONE;
+		Overlay overlay = imp.getOverlay();
+		if (imp.getHideOverlay())
+			overlay = null;
+		if (overlay!=null)
+			overlay = overlay.duplicate();
+		Overlay overlay2 = new Overlay();
 		for (int i=1; i<=nSlices; i++) {
 			IJ.showStatus("Scale: " + i + "/" + nSlices);
 			ip1 = stack1.getProcessor(i);
@@ -88,6 +102,18 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 			ip2 = ip1.resize(newWidth, newHeight, averageWhenDownsizing);
 			if (ip2!=null)
 				stack2.addSlice(label, ip2);
+			if (overlay!=null) {
+				Roi roi = overlay.get(i-1);
+				Rectangle bounds = roi.getBounds();
+				if (roi instanceof ImageRoi && bounds.x==0 && bounds.y==0) {
+					ImageRoi iroi = (ImageRoi)roi;
+					ImageProcessor processor = iroi.getProcessor();
+					processor.setInterpolationMethod(method);
+					processor =processor.resize(newWidth, newHeight, averageWhenDownsizing);
+					iroi.setProcessor(processor);
+					overlay2.add(iroi);
+				}
+			}
 			IJ.showProgress(i, nSlices);
 		}
 		imp2.setStack(title, stack2);
@@ -96,6 +122,8 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 			cal.pixelWidth *= 1.0/xscale;
 			cal.pixelHeight *= 1.0/yscale;
 		}
+		if (overlay2.size()>0)
+			imp2.setOverlay(overlay2);
 		IJ.showProgress(1.0);
 		int[] dim = imp.getDimensions();
 		imp2.setDimensions(dim[2], dim[3], dim[4]);
@@ -116,7 +144,7 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 		}
 	}
 
-	void scale(ImageProcessor ip) {
+	private void scale(ImageProcessor ip, Overlay overlay) {
 		if (newWindow) {
 			Rectangle r = ip.getRoi();
 			ImagePlus imp2 = imp.createImagePlus();
@@ -126,6 +154,18 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 				cal.pixelWidth *= 1.0/xscale;
 				cal.pixelHeight *= 1.0/yscale;
 			}
+			if (overlay!=null) {
+				Roi roi = overlay.get(0);
+				Rectangle bounds = roi.getBounds();
+				if (roi instanceof ImageRoi && bounds.x==0 && bounds.y==0) {
+					ImageRoi iroi = (ImageRoi)roi;
+					ImageProcessor processor = iroi.getProcessor();
+					processor.setInterpolationMethod(interpolationMethod);
+					processor =processor.resize(newWidth, newHeight, averageWhenDownsizing);
+					iroi.setProcessor(processor);
+					imp2.setOverlay(new Overlay(iroi));
+				}
+			}
 			imp2.show();
 			imp.trimProcessor();
 			imp2.trimProcessor();
diff --git a/ij/plugin/SimpleCommands.java b/ij/plugin/SimpleCommands.java
index 2c8b428..f6fd177 100644
--- a/ij/plugin/SimpleCommands.java
+++ b/ij/plugin/SimpleCommands.java
@@ -90,9 +90,7 @@ public class SimpleCommands implements PlugIn {
 		GenericDialog gd = new GenericDialog("Rename");
 		gd.addStringField("Title:", imp.getTitle(), 30);
 		gd.showDialog();
-		if (gd.wasCanceled())
-			return;
-		else
+		if (!gd.wasCanceled())
 			imp.setTitle(gd.getNextString());
 	}
 		
diff --git a/ij/plugin/StackEditor.java b/ij/plugin/StackEditor.java
index 9a84557..b905a94 100644
--- a/ij/plugin/StackEditor.java
+++ b/ij/plugin/StackEditor.java
@@ -261,6 +261,7 @@ public class StackEditor implements PlugIn {
 		Calibration cal = imp.getCalibration();
 		CompositeImage cimg = imp.isComposite()?(CompositeImage)imp:null;
 		if (imp.getNChannels()!=imp.getStackSize()) cimg = null;
+		Overlay overlay = imp.getOverlay();
 		for (int i=1; i<=size; i++) {
 			String label = stack.getShortSliceLabel(i);
 			String title = label!=null&&!label.equals("")?label:getTitle(imp, i);
@@ -277,6 +278,19 @@ public class StackEditor implements PlugIn {
 			String info = stack.getSliceLabel(i);
 			if (info!=null && !info.equals(label))
 				imp2.setProperty("Info", info);
+			imp2.setIJMenuBar(i==size);
+			if (overlay!=null) {
+				Overlay overlay2 = new Overlay();
+				for (int j=0; j<overlay.size(); j++) {
+					Roi roi = overlay.get(j);
+					if (roi.getPosition()==i) {
+						roi.setPosition(0);
+						overlay2.add((Roi)roi.clone());
+					}
+				}
+				if (overlay2.size()>0)
+					imp2.setOverlay(overlay2);
+			}
 			imp2.show();
 		}
 		imp.changes = false;
diff --git a/ij/plugin/WindowOrganizer.java b/ij/plugin/WindowOrganizer.java
index c17c781..e07c03b 100644
--- a/ij/plugin/WindowOrganizer.java
+++ b/ij/plugin/WindowOrganizer.java
@@ -39,6 +39,10 @@ public class WindowOrganizer implements PlugIn {
 			ImageWindow win = getWindow(wList[i]);
 			if (win==null)
 				continue;
+			if (win instanceof PlotWindow && !((PlotWindow)win).getPlot().isFrozen()) {
+				IJ.error("Tile", "Unfrozen plot windows cannot be tiled.");
+				return;
+			}
 			Dimension d = win.getSize();
 			int w = d.width;
 			int h = d.height + titlebarHeight;
@@ -109,6 +113,8 @@ public class WindowOrganizer implements PlugIn {
 				while (win.getSize().width*0.85>=tileWidth && canvas.getMagnification()>0.03125)
 					canvas.zoomOut(0, 0);
 				win.toFront();
+				ImagePlus imp = win.getImagePlus();
+				if (imp!=null) imp.setIJMenuBar(i==nPics-1);
 			}
 			hloc += tileWidth + GAP;
 		}
@@ -149,6 +155,8 @@ public class WindowOrganizer implements PlugIn {
 			win.toFront();
 				x += XOFFSET;
 			y += YOFFSET;
+			ImagePlus imp = win.getImagePlus();
+			if (imp!=null) imp.setIJMenuBar(i==wList.length-1);
 		}
 	}
 	
diff --git a/ij/plugin/filter/AVI_Writer.java b/ij/plugin/filter/AVI_Writer.java
index 1eb885c..e7be93e 100644
--- a/ij/plugin/filter/AVI_Writer.java
+++ b/ij/plugin/filter/AVI_Writer.java
@@ -61,7 +61,7 @@ public class AVI_Writer implements PlugInFilter {
     //compression options: dialog parameters
     private int      compressionIndex = 2; //0=none, 1=PNG, 2=JPEG
     private static int      jpegQuality = 90;    //0 is worst, 100 best (not currently used)
-    private final static String[] COMPRESSION_STRINGS = new String[] {"Uncompressed", "PNG", "JPEG"};
+    private final static String[] COMPRESSION_STRINGS = new String[] {"None", "PNG", "JPEG"};
     private final static int[] COMPRESSION_TYPES = new int[] {NO_COMPRESSION, PNG_COMPRESSION, JPEG_COMPRESSION};
 
     private ImagePlus imp;
@@ -114,24 +114,23 @@ public class AVI_Writer implements PlugInFilter {
 
     private boolean showDialog(ImagePlus imp) {
     	String options = Macro.getOptions();
-    	if (options!=null && options.indexOf("compression=")==-1)
-    		Macro.setOptions("compression=Uncompressed "+options);
+    	if (options!=null) {
+    		if (!options.contains("compression="))
+    			options = "compression=JPEG "+options;
+    		options = options.replace("compression=Uncompressed", "compression=None");
+    		Macro.setOptions(options);
+    	}
   		double fps = getFrameRate(imp);
  		int decimalPlaces = (int) fps == fps?0:1;
         GenericDialog gd = new GenericDialog("Save as AVI...");
         gd.addChoice("Compression:", COMPRESSION_STRINGS, COMPRESSION_STRINGS[compressionIndex]);
-        //gd.addNumericField("JPEG Quality (0-100):", jpegQuality, 0, 3, "");
 		gd.addNumericField("Frame Rate:", fps, decimalPlaces, 3, "fps");
         gd.showDialog();                            // user input (or reading from macro) happens here
         if (gd.wasCanceled())                       // dialog cancelled?
             return false;
-        gd.setSmartRecording(compressionIndex==2);
         compressionIndex = gd.getNextChoiceIndex();
-        gd.setSmartRecording(false);
-        //jpegQuality = (int)gd.getNextNumber();
         fps = gd.getNextNumber();
         if (fps<=0.5) fps = 0.5;
-        //if (fps>60.0) fps = 60.0;
 		imp.getCalibration().fps = fps;
 		return true;
     }
@@ -191,8 +190,9 @@ public class AVI_Writer implements PlugInFilter {
         int microSecPerFrame = (int)Math.round((1.0/getFrameRate(imp))*1.0e6);
         int dwChunkId = biCompression==NO_COMPRESSION ? FOURCC_00db : FOURCC_00dc;
         long sizeEstimate = bytesPerPixel*xDim*yDim*(long)zDim;
+        //boolean writeAVI2index = true;//frameDataSize*zDim > 1000000000;
         int nAvixChunksEstimate = (int)(sizeEstimate/JUNK_SIZE_THRESHOLD);  //estimated number of AVIX junks
-        endHeadPointer = 4096+((nAvixChunksEstimate*16+1000)/1024)*1024;       //reserve plenty of space for 'indx'
+        endHeadPointer = 4096+((nAvixChunksEstimate*16+1000)/1024)*1024;    //reserve plenty of space for 'indx'
 
         //  W r i t e   A V I   f i l e   h e a d e r
         writeString("RIFF");    // signature
@@ -206,7 +206,7 @@ public class AVI_Writer implements PlugInFilter {
                                 // the first 8 bytes for avihSignature and the length
         writeInt(microSecPerFrame); // dwMicroSecPerFrame - Write the microseconds per frame
         writeInt(0);            // dwMaxBytesPerSec (maximum data rate of the file in bytes per second)
-        writeInt(0);            // dwReserved1 - Reserved1 field set to zero
+        writeInt(0);            // dwPaddingGranularity (for header length?), previously dwReserved1, usually set to zero.
         writeInt(0x10);         // dwFlags - just set the bit for AVIF_HASINDEX
                                 //   10H AVIF_HASINDEX: The AVI file has an idx1 chunk containing
                                 //   an index at the end of the file.  For good performance, all
@@ -311,6 +311,7 @@ public class AVI_Writer implements PlugInFilter {
         int currentFilePart = 0;// 0 is inside RIFF AVI (AVI 1.0 compatible), >0 is RIFF AVIX (data chunk of AVI 2.0)
 
         //  W r i t e   f r a m e   d a t a   a n d   i n d i c e s
+        boolean writeAVI2index = false; // see whether we need an AVI2 index (large files only)
         int iFrame = 0;
         while (iFrame < zDim) {
             if (currentFilePart > 0) {  // open new RIFF AVIX chunk
@@ -373,25 +374,29 @@ public class AVI_Writer implements PlugInFilter {
             int nFramesInChunk = iFrame - firstFrameInChunk;
 
             //  W r i t e   A V I - 2   I n d e x
-            long ix00pointer = raFile.getFilePointer();
-            writeString("ix00");        // AVI 2.0 style index of frames within the chunk
-            chunkSizeHere();            // size of ix00 chunk (nesting level 2)
-            writeShort(2);              // wLongsPerEntry = 2 ('Longs' are 32-bit here!)
-            writeByte(0);               // bIndexSubType=0
-            writeByte(1);               // bIndexType=1: AVI_INDEX_OF_CHUNKS
-            writeInt(nFramesInChunk);   // nEntriesInUse
-            writeInt(dwChunkId);        // dwChunkId, '00dc' or '00db'
-            writeLong(moviPointer);     // qwBaseOffset
-            writeInt(0);                // dwReserved, first two are qwBaseOffset?
-            for (int z=firstFrameInChunk; z<iFrame; z++) {
-                writeInt(dataChunkOffset[z]+8); //note: AVI--2 index points to chunk data, not chunk header
-                writeInt(dataChunkLength[z]);   //length without chunk header
-            }
-            //IJ.log("write ix00: frames "+firstFrameInChunk+"-"+(iFrame-1)+" offset "+Long.toHexString(dataChunkOffset[firstFrameInChunk])+"-"+Long.toHexString(dataChunkOffset[iFrame-1]));
-            //enter this ix00 index to index of indices:
-            writeMainIndxEntry(ix00pointer, (int)(raFile.getFilePointer()-ix00pointer), nFramesInChunk);
+            if (iFrame < zDim)
+                writeAVI2index = true;      //can't write everything the first time? Then we need the AVI 2 format.
+            if (writeAVI2index) {
+                long ix00pointer = raFile.getFilePointer();
+                writeString("ix00");        // AVI 2.0 style index of frames within the chunk
+                chunkSizeHere();            // size of ix00 chunk (nesting level 2)
+                writeShort(2);              // wLongsPerEntry = 2 ('Longs' are 32-bit here!)
+                writeByte(0);               // bIndexSubType=0
+                writeByte(1);               // bIndexType=1: AVI_INDEX_OF_CHUNKS
+                writeInt(nFramesInChunk);   // nEntriesInUse
+                writeInt(dwChunkId);        // dwChunkId, '00dc' or '00db'
+                writeLong(moviPointer);     // qwBaseOffset
+                writeInt(0);                // dwReserved, first two are qwBaseOffset?
+                for (int z=firstFrameInChunk; z<iFrame; z++) {
+                    writeInt(dataChunkOffset[z]+8); //note: AVI--2 index points to chunk data, not chunk header
+                    writeInt(dataChunkLength[z]);   //length without chunk header
+                }
+                //IJ.log("write ix00: frames "+firstFrameInChunk+"-"+(iFrame-1)+" offset "+Long.toHexString(dataChunkOffset[firstFrameInChunk])+"-"+Long.toHexString(dataChunkOffset[iFrame-1]));
+                //enter this ix00 index to index of indices:
+                writeMainIndxEntry(ix00pointer, (int)(raFile.getFilePointer()-ix00pointer), nFramesInChunk);
 
-            chunkEndWriteSize();        // 'ix00' finished (nesting level 2)
+                chunkEndWriteSize();        // 'ix00' finished (nesting level 2)
+            }
             chunkEndWriteSize();        // LIST 'movi' finished (nesting level 1)
 
             //  W r i t e   A V I - 1   I n d e x
@@ -418,6 +423,15 @@ public class AVI_Writer implements PlugInFilter {
             chunkEndWriteSize();    // 'RIFF' File finished (nesting level 0)
             currentFilePart++;
         } //while (iFrame < zDim)
+
+        if (!writeAVI2index) {      //delete main AVI 2 index prepared previously
+            raFile.seek(pointer2indx);
+            writeString("JUNK");        // overwrite 'indx'
+            chunkSizeHere();            // size of 'JUNK' for padding goes here
+            raFile.seek(endHeadPointer);// end of the padded range
+            chunkEndWriteSize();        // 'JUNK' finished              
+        }
+
         raFile.close();
         IJ.showProgress(1.0);
 		if (isComposite || isHyperstack)
diff --git a/ij/plugin/filter/Analyzer.java b/ij/plugin/filter/Analyzer.java
index 0380147..e6551cb 100644
--- a/ij/plugin/filter/Analyzer.java
+++ b/ij/plugin/filter/Analyzer.java
@@ -355,7 +355,8 @@ public class Analyzer implements PlugInFilter, Measurements {
 	
 	void measurePoint(Roi roi) {
 		if (rt.size()>0) {
-			if (!IJ.isResultsWindow()) reset();
+			if (!IJ.isResultsWindow())
+				reset();
 			int index = rt.getColumnIndex("X");
 			if (index<0 || !rt.columnExists(index)) {
 				clearSummary();
@@ -365,11 +366,23 @@ public class Analyzer implements PlugInFilter, Measurements {
 		FloatPolygon p = roi.getFloatPolygon();
 		ImagePlus imp2 = isRedirectImage()?getRedirectImageOrStack(imp):null;
 		if (imp2==null) imp2 = imp;
+		ImageStack stack = null;
+		if (imp2.getStackSize()>1)
+			stack = imp2.getStack();
 		for (int i=0; i<p.npoints; i++) {
-			ImageProcessor ip = imp2.getProcessor();
+			int position = 0;
+			if (roi instanceof PointRoi)
+				position = ((PointRoi)roi).getPointPosition(i);
+			ImageProcessor ip = null;
+			if (stack!=null && position>0 && position<=stack.size())
+				ip = stack.getProcessor(position);
+			else
+				ip = imp2.getProcessor();
 			ip.setRoi((int)p.xpoints[i], (int)p.ypoints[i], 1, 1);
 			ImageStatistics stats = ImageStatistics.getStatistics(ip, measurements, imp2.getCalibration());
-			saveResults(stats, new PointRoi(p.xpoints[i], p.ypoints[i]));
+			PointRoi point = new PointRoi(p.xpoints[i], p.ypoints[i]);
+			point.setPosition(position);
+			saveResults(stats, point);
 			if (i!=p.npoints-1) displayResults();
 		}
 	}
@@ -708,15 +721,28 @@ public class Analyzer implements PlugInFilter, Measurements {
 		}
 		rt.addValue("X", cal.getX(x));
 		rt.addValue("Y", cal.getY(y, imp.getHeight()));
+		int position = roi.getPosition();
 		if (imp.isHyperStack() || imp.isComposite()) {
+			int channel = imp.getChannel();
+			int slice = imp.getSlice();
+			int frame = imp.getFrame();
+			if (position>0) {
+				int[] pos = imp.convertIndexToPosition(position);
+				channel = pos[0];
+				slice = pos[1];
+				frame = pos[2];
+			}
 			if (imp.getNChannels()>1)
-				rt.addValue("Ch", imp.getChannel());
+				rt.addValue("Ch", channel);
 			if (imp.getNSlices()>1)
-				rt.addValue("Slice", imp.getSlice());
+				rt.addValue("Slice", slice);
 			if (imp.getNFrames()>1)
-				rt.addValue("Frame", imp.getFrame());
-		} else if (imp.getStackSize()>1)
-			rt.addValue("Slice", cal.getZ(imp.getCurrentSlice()));
+				rt.addValue("Frame", frame);
+		} else if (imp.getStackSize()>1) {
+			if (position==0)
+				position = imp.getCurrentSlice();
+			rt.addValue("Slice", position);
+		}
 		if (imp.getProperty("FHT")!=null) {
 			double center = imp.getWidth()/2.0;
 			y = imp.getHeight()-y-1;
@@ -728,8 +754,6 @@ public class Analyzer implements PlugInFilter, Measurements {
 			rt.addValue("R", (imp.getWidth()/r)*cal.pixelWidth);
 			rt.addValue("Theta", theta);
 		}
-		//if ((measurements&MEAN)==0)
-		//	rt.addValue("Mean", value);
 	}
 
 	String getFileName() {
diff --git a/ij/plugin/filter/GaussianBlur.java b/ij/plugin/filter/GaussianBlur.java
index 8534d1b..ee84a53 100644
--- a/ij/plugin/filter/GaussianBlur.java
+++ b/ij/plugin/filter/GaussianBlur.java
@@ -144,6 +144,17 @@ public class GaussianBlur implements ExtendedPlugInFilter, DialogListener {
         return true;
     }
 
+	/** Gaussian Filtering of an ImageProcessor
+	* @param ip       The ImageProcessor to be filtered.
+	* @param sigma   Standard deviation of the Gaussian (pixels)
+	*
+	* @see ij.process.ImageProcessor#blurGaussian(double)
+	*/
+	public void blurGaussian(ImageProcessor ip, double sigma) {
+		double accuracy = (ip instanceof ByteProcessor||ip instanceof ColorProcessor)?0.002:0.0002;
+		blurGaussian(ip, sigma, sigma, accuracy);
+	}
+
     /** Gaussian Filtering of an ImageProcessor
      * @param ip       The ImageProcessor to be filtered.
      * @param sigmaX   Standard deviation of the Gaussian in x direction (pixels)
diff --git a/ij/plugin/filter/ImageMath.java b/ij/plugin/filter/ImageMath.java
index 3a74b48..d16f0b4 100644
--- a/ij/plugin/filter/ImageMath.java
+++ b/ij/plugin/filter/ImageMath.java
@@ -169,7 +169,8 @@ public class ImageMath implements ExtendedPlugInFilter, DialogListener {
 			lower = ip.getMinThreshold();
 			upper = ip.getMaxThreshold();
 			if (lower==ImageProcessor.NO_THRESHOLD || !(ip instanceof FloatProcessor)) {
-				IJ.error("Thresholded 32-bit float image required");
+				String title = imp!=null?"\n\""+imp.getTitle()+"\"":"";
+				IJ.error("NaN Backround", "Thresholded 32-bit float image required:"+title);
 				canceled = true;
 				return;
 			}
@@ -358,8 +359,7 @@ public class ImageMath implements ExtendedPlugInFilter, DialogListener {
 					if (hasA) interp.setVariable("a", getA((h-y-1)-h2, x-w2));
 					if (hasD) interp.setVariable("d", getD(x-w2,y-h2));
 					interp.run(PCStart);
-					v2 = (int)interp.getVariable("v");
-					pixels2[index] = (float)v2;
+					pixels2[index] = (float)interp.getVariable("v");
 				}
 			}
 			if (hasGetPixel) System.arraycopy(pixels2, 0, pixels1, 0, w*h);
diff --git a/ij/plugin/filter/Info.java b/ij/plugin/filter/Info.java
index c4efbbe..e7116f0 100644
--- a/ij/plugin/filter/Info.java
+++ b/ij/plugin/filter/Info.java
@@ -1,17 +1,12 @@
 package ij.plugin.filter;
-import ij.*;
-import ij.gui.*;
-import ij.process.*;
-import ij.measure.*;
-import ij.io.*;
-import ij.util.Tools;
-import ij.plugin.frame.Editor;
-import ij.text.TextWindow;
-import java.awt.*;
-import java.util.*;
-import java.lang.reflect.*;
-
-/** This plugin implements the Image/Show Info command. */
+import ij.ImagePlus;
+import ij.process.ImageProcessor;
+import ij.plugin.ImageInfo;
+
+/**
+* @deprecated
+* replaced by ij.plugin.ImageInfo
+*/
 public class Info implements PlugInFilter {
     private ImagePlus imp;
 
@@ -21,377 +16,11 @@ public class Info implements PlugInFilter {
 	}
 
 	public void run(ImageProcessor ip) {
-		String info = getImageInfo(imp, ip);
-		if (info.indexOf("----")>0)
-			showInfo(info, 450, 500);
-		else {
-			int inc = info.contains("No Selection")?0:75;
-			showInfo(info, 300, 350+inc);
-		}
-	}
-
-	public String getImageInfo(ImagePlus imp, ImageProcessor ip) {
-		String infoProperty = null;
-		if (imp.getStackSize()>1) {
-			ImageStack stack = imp.getStack();
-			String label = stack.getSliceLabel(imp.getCurrentSlice());
-			if (label!=null && label.indexOf('\n')>0)
-				infoProperty = label;
-		}
-		if (infoProperty==null) {
-			infoProperty = (String)imp.getProperty("Info");
-			if (infoProperty==null)
-				infoProperty = getExifData(imp);
-		}
-		String info = getInfo(imp, ip);
-		if (infoProperty!=null)
-			return infoProperty + "\n------------------------------------------------------\n" + info;
-		else
-			return info;		
 	}
 	
-	private String getExifData(ImagePlus imp) {
-		FileInfo fi = imp.getOriginalFileInfo();
-		if (fi==null)
-			return null;
-		String directory = fi.directory;
-		String name = fi.fileName;
-		if (directory==null)
-			return null;
-		if ((name==null||name.equals("")) && imp.getStack().isVirtual())
-			name = imp.getStack().getSliceLabel(imp.getCurrentSlice());
-		if (name==null || !(name.endsWith("jpg")||name.endsWith("JPG")))
-			return null;
-		String path = directory + name;
-		String metadata = null;
-		try {
-			Class c = IJ.getClassLoader().loadClass("Exif_Reader");
-			if (c==null) return null;
-			String methodName = "getMetadata";
-			Class[] argClasses = new Class[1];
-			argClasses[0] = methodName.getClass();
-			Method m = c.getMethod("getMetadata", argClasses);
-			Object[] args = new Object[1];
-			args[0] = path;
-			Object obj = m.invoke(null, args);
-			metadata = obj!=null?obj.toString():null;
-		} catch(Exception e) {
-			return null;
-		}
-		if (metadata!=null && !metadata.startsWith("Error:"))
-			return metadata;
-		else
-			return null;
-	}
-
-	String getInfo(ImagePlus imp, ImageProcessor ip) {
-		String s = new String("");
-		if (IJ.getInstance()!=null)
-			s += IJ.getInstance().getInfo()+"\n \n";
-		s += "Title: " + imp.getTitle() + "\n";
-		Calibration cal = imp.getCalibration();
-    	int stackSize = imp.getStackSize();
-    	int channels = imp.getNChannels();
-    	int slices = imp.getNSlices();
-    	int frames = imp.getNFrames();
-		int digits = imp.getBitDepth()==32?4:0;
-		int dp, dp2;
-		boolean nonUniformUnits = !cal.getXUnit().equals(cal.getYUnit());
-		String xunit = cal.getXUnit();
-		String yunit = cal.getYUnit();
-		String zunit = cal.getZUnit();
-		if (cal.scaled()) {
-			String xunits = cal.getUnits();
-			String yunits = xunits;
-			String zunits = xunits;
-			if (nonUniformUnits) {
-				xunits = xunit;
-				yunits = yunit;
-				zunits = zunit;
-			}
-			double pw = imp.getWidth()*cal.pixelWidth;
-			double ph = imp.getHeight()*cal.pixelHeight;
-	    	s += "Width:  "+d2s(pw)+" " + xunits+" ("+imp.getWidth()+")\n";
-	    	s += "Height:  "+d2s(ph)+" " + yunits+" ("+imp.getHeight()+")\n";
-	    	if (slices>1) {
-				double pd = slices*cal.pixelDepth;
-	    		s += "Depth:  "+d2s(pd)+" " + zunits+" ("+slices+")\n";
-	    	}
-			s += "Size:  "+ImageWindow.getImageSize(imp)+"\n";
-	    	double xResolution = 1.0/cal.pixelWidth;
-	    	double yResolution = 1.0/cal.pixelHeight;
-	    	if (xResolution==yResolution)
-	    		s += "Resolution:  "+d2s(xResolution) + " pixels per "+xunit+"\n";
-	    	else {
-	    		s += "X Resolution:  "+d2s(xResolution) + " pixels per "+xunit+"\n";
-	    		s += "Y Resolution:  "+d2s(yResolution) + " pixels per "+yunit+"\n";
-	    	}
-	    } else {
-	    	s += "Width:  " + imp.getWidth() + " pixels\n";
-	    	s += "Height:  " + imp.getHeight() + " pixels\n";
-	    	if (stackSize>1)
-	    		s += "Depth:  " + slices + " pixels\n";
-			s += "Size:  "+ImageWindow.getImageSize(imp)+"\n";
-	    }
-    	if (stackSize>1) {
-    		String vunit = cal.getUnit()+"^3";
-    		if (nonUniformUnits)
-    			vunit = "("+xunit+" x "+yunit+" x "+zunit+")";
-	    	s += "Voxel size: "+d2s(cal.pixelWidth)+"x"+d2s(cal.pixelHeight)+"x"+d2s(cal.pixelDepth)+" "+vunit+"\n";
-	    } else {
-    		String punit = cal.getUnit()+"^2";
-    		if (nonUniformUnits)
-    			punit = "("+xunit+" x "+yunit+")";
-	    	dp = Tools.getDecimalPlaces(cal.pixelWidth, cal.pixelHeight);
-	    	s += "Pixel size: "+d2s(cal.pixelWidth)+"x"+d2s(cal.pixelHeight)+" "+punit+"\n";
-	    }
-
-	    s += "ID: "+imp.getID()+"\n";
-	    int type = imp.getType();
-    	switch (type) {
-	    	case ImagePlus.GRAY8:
-	    		s += "Bits per pixel: 8 ";
-	    		String lut = "LUT";
-	    		if (imp.getProcessor().isColorLut())
-	    			lut = "color " + lut;
-	    		else
-	    			lut = "grayscale " + lut;
-	    		if (imp.isInvertedLut())
-	    			lut = "inverting " + lut;
-				s += "(" + lut + ")\n";
-				if (imp.getNChannels()>1)
-					s += displayRanges(imp);
-				else
-					s += "Display range: "+(int)ip.getMin()+"-"+(int)ip.getMax()+"\n";
-				break;
-	    	case ImagePlus.GRAY16: case ImagePlus.GRAY32:
-	    		if (type==ImagePlus.GRAY16) {
-	    			String sign = cal.isSigned16Bit()?"signed":"unsigned";
-	    			s += "Bits per pixel: 16 ("+sign+")\n";
-	    		} else
-	    			s += "Bits per pixel: 32 (float)\n";
-				if (imp.getNChannels()>1)
-					s += displayRanges(imp);
-				else {
-					s += "Display range: ";
-					double min = ip.getMin();
-					double max = ip.getMax();
-					if (cal.calibrated()) {
-						min = cal.getCValue((int)min);
-						max = cal.getCValue((int)max);
-					}
-					s += d2s(min) + " - " + d2s(max) + "\n";
-				}
-				break;
-	    	case ImagePlus.COLOR_256:
-	    		s += "Bits per pixel: 8 (color LUT)\n";
-	    		break;
-	    	case ImagePlus.COLOR_RGB:
-	    		s += "Bits per pixel: 32 (RGB)\n";
-	    		break;
-    	}
-		double interval = cal.frameInterval;	
-		double fps = cal.fps;	
-    	if (stackSize>1) {
-    		ImageStack stack = imp.getStack();
-    		int slice = imp.getCurrentSlice();
-    		String number = slice + "/" + stackSize;
-    		String label = stack.getShortSliceLabel(slice);
-    		if (label!=null && label.length()>0)
-    			label = " (" + label + ")";
-    		else
-    			label = "";
-			if (interval>0.0 || fps!=0.0) {
-				s += "Frame: " + number + label + "\n";
-				if (fps!=0.0) {
-					String sRate = Math.abs(fps-Math.round(fps))<0.00001?IJ.d2s(fps,0):IJ.d2s(fps,5);
-					s += "Frame rate: " + sRate + " fps\n";
-				}
-				if (interval!=0.0)
-					s += "Frame interval: " + ((int)interval==interval?IJ.d2s(interval,0):IJ.d2s(interval,5)) + " " + cal.getTimeUnit() + "\n";
-			} else
-				s += "Image: " + number + label + "\n";
-			if (imp.isHyperStack()) {
-				if (channels>1)
-					s += "  Channel: " + imp.getChannel() + "/" + channels + "\n";
-				if (slices>1)
-					s += "  Slice: " + imp.getSlice() + "/" + slices + "\n";
-				if (frames>1)
-					s += "  Frame: " + imp.getFrame() + "/" + frames + "\n";
-			}
-			if (imp.isComposite()) {
-				if (!imp.isHyperStack() && channels>1)
-					s += "  Channels: " + channels + "\n";
-				String mode = ((CompositeImage)imp).getModeAsString();
-				s += "  Composite mode: \"" + mode + "\"\n";
-			}
-		}
-
-		if (ip.getMinThreshold()==ImageProcessor.NO_THRESHOLD)
-	    	s += "No threshold\n";
-	    else {
-	    	double lower = ip.getMinThreshold();
-	    	double upper = ip.getMaxThreshold();
-	    	String uncalibrated = "";
-			if (cal.calibrated()) {
-				uncalibrated = " ("+(int)lower+"-"+(int)upper+")";
-				lower = cal.getCValue((int)lower);
-				upper = cal.getCValue((int)upper);
-			}
-			s += "Threshold: "+d2s(lower)+"-"+d2s(upper)+uncalibrated+"\n";
-		}
-		ImageCanvas ic = imp.getCanvas();
-    	double mag = ic!=null?ic.getMagnification():1.0;
-    	if (mag!=1.0)
-			s += "Magnification: " + IJ.d2s(mag,2) + "\n";
-			
-	    if (cal.calibrated()) {
-	    	s += " \n";
-	    	int curveFit = cal.getFunction();
-			s += "Calibration function: ";
-			if (curveFit==Calibration.UNCALIBRATED_OD)
-				s += "Uncalibrated OD\n";	    	
-			else if (curveFit==Calibration.CUSTOM)
-				s += "Custom lookup table\n";	    	
-			else
-				s += CurveFitter.fList[curveFit]+"\n";
-			double[] c = cal.getCoefficients();
-			if (c!=null) {
-				s += "  a: "+IJ.d2s(c[0],6)+"\n";
-				s += "  b: "+IJ.d2s(c[1],6)+"\n";
-				if (c.length>=3)
-					s += "  c: "+IJ.d2s(c[2],6)+"\n";
-				if (c.length>=4)
-					s += "  c: "+IJ.d2s(c[3],6)+"\n";
-				if (c.length>=5)
-					s += "  c: "+IJ.d2s(c[4],6)+"\n";
-			}
-			s += "  Unit: \""+cal.getValueUnit()+"\"\n";	    	
-	    } else
-	    	s += "Uncalibrated\n";
-
-	    FileInfo fi = imp.getOriginalFileInfo();
-		if (fi!=null) {
-			if (fi.url!=null && !fi.url.equals(""))
-				s += "URL: " + fi.url + "\n";
-			else if (fi.directory!=null && fi.fileName!=null)
-				s += "Path: " + fi.directory + fi.fileName + "\n";
-		}
-		
-		ImageWindow win = imp.getWindow();
-		if (win!=null) {
-			Point loc = win.getLocation();
-			Dimension screen = IJ.getScreenSize();
-			s += "Screen location: "+loc.x+","+loc.y+" ("+screen.width+"x"+screen.height+")\n";
-		}
-		
-		String zOrigin = stackSize>1||cal.zOrigin!=0.0?","+d2s(cal.zOrigin):"";
-		String origin = d2s(cal.xOrigin)+","+d2s(cal.yOrigin)+zOrigin;
-		if (!origin.equals("0,0") || cal.getInvertY())
-	    	s += "Coordinate origin:  "+origin+"\n";
-	    if (cal.getInvertY())
-	    	s += "Inverted y coordinates\n";
-
-	    Overlay overlay = imp.getOverlay();
-		if (overlay!=null) {
-			String hidden = imp.getHideOverlay()?" (hidden)":" ";
-			int n = overlay.size();
-			String elements = n==1?" element":" elements";
-			s += "Overlay: " + n + elements + (imp.getHideOverlay()?" (hidden)":"") + "\n";
-		} else
-	    	s += "No overlay\n";
-
-	    Roi roi = imp.getRoi();
-	    if (roi == null) {
-			if (cal.calibrated())
-	    		s += " \n";
-	    	s += "No selection\n";
-	    } else if (roi instanceof EllipseRoi) {
-	    	s += "\nElliptical selection\n";
-	    	double[] p = ((EllipseRoi)roi).getParams();
-			double dx = p[2] - p[0];
-			double dy = p[3] - p[1];
-			double major = Math.sqrt(dx*dx+dy*dy);
-			s += "  Major: " + IJ.d2s(major,2) + "\n";
-			s += "  Minor: " + IJ.d2s(major*p[4],2) + "\n";
-			s += "  X1: " + IJ.d2s(p[0],2) + "\n";
-			s += "  Y1: " + IJ.d2s(p[1],2) + "\n";
-			s += "  X2: " + IJ.d2s(p[2],2) + "\n";
-			s += "  Y2: " + IJ.d2s(p[3],2) + "\n";
-			s += "  Aspect ratio: " + IJ.d2s(p[4],2) + "\n";
-	    } else {
-	    	s += " \n";
-	    	s += roi.getTypeAsString()+" Selection";
-	    	String points = null;
-			if (roi instanceof PointRoi) {
-				int npoints = ((PolygonRoi)roi).getNCoordinates();
-				String suffix = npoints>1?"s)":")";
-				points = " (" + npoints + " point" + suffix;
-			}
-    		String name = roi.getName();
-    		if (name!=null) {
-				s += " (\"" + name + "\")";
-				if (points!=null) s += "\n " + points;		
-			} else if (points!=null)
-				s += points;
-			s += "\n";		
-	    	Rectangle r = roi.getBounds();
-	    	if (roi instanceof Line) {
-	    		Line line = (Line)roi;
-	    		s += "  X1: " + IJ.d2s(line.x1d*cal.pixelWidth) + "\n";
-	    		s += "  Y1: " + IJ.d2s(yy(line.y1d,imp)*cal.pixelHeight) + "\n";
-	    		s += "  X2: " + IJ.d2s(line.x2d*cal.pixelWidth) + "\n";
-	    		s += "  Y2: " + IJ.d2s(yy(line.y2d,imp)*cal.pixelHeight) + "\n";
-			} else if (cal.scaled()) {
-				s += "  X: " + IJ.d2s(cal.getX(r.x)) + " (" + r.x + ")\n";
-				s += "  Y: " + IJ.d2s(cal.getY(r.y,imp.getHeight())) + " (" +  r.y + ")\n";
-				s += "  Width: " + IJ.d2s(r.width*cal.pixelWidth) + " (" +  r.width + ")\n";
-				s += "  Height: " + IJ.d2s(r.height*cal.pixelHeight) + " (" +  r.height + ")\n";
-			} else {
-				s += "  X: " + r.x + "\n";
-				s += "  Y: " + yy(r.y,imp) + "\n";
-				s += "  Width: " + r.width + "\n";
-				s += "  Height: " + r.height + "\n";
-			}
-	    }
-	    
-		return s;
-	}
-	
-	private String displayRanges(ImagePlus imp) {
-		LUT[] luts = imp.getLuts();
-		if (luts==null)
-			return "";
-		String s = "Display ranges\n";
-		int n = luts.length;
-		if (n>7) n=7;
-		for (int i=0; i<n; i++) {
-			double min = luts[i].min;
-			double max = luts[i].max;
-			s += "  " + (i+1) + ": " + d2s(min) + "-" + d2s(max) + "\n";
-		}
-		return s;
-	}
-	
-	// returns a Y coordinate based on the "Invert Y Coodinates" flag
-	int yy(int y, ImagePlus imp) {
-		return Analyzer.updateY(y, imp.getHeight());
-	}
-
-	// returns a Y coordinate based on the "Invert Y Coodinates" flag
-	double yy(double y, ImagePlus imp) {
-		return Analyzer.updateY(y, imp.getHeight());
-	}
-
-	void showInfo(String info, int width, int height) {
-		new TextWindow("Info for "+imp.getTitle(), info, width, height);
-		//Editor ed = new Editor();
-		//ed.setSize(width, height);
-		//ed.create("Info for "+imp.getTitle(), info);
+	public String getImageInfo(ImagePlus imp, ImageProcessor ip) {
+		ImageInfo info = new ImageInfo();
+		return info.getImageInfo(imp);
 	}
-	
-    private String d2s(double n) {
-		return IJ.d2s(n,Tools.getDecimalPlaces(n));
-    }
 
 }
diff --git a/ij/plugin/filter/MaximumFinder.java b/ij/plugin/filter/MaximumFinder.java
index 1206179..7d240cf 100644
--- a/ij/plugin/filter/MaximumFinder.java
+++ b/ij/plugin/filter/MaximumFinder.java
@@ -684,6 +684,8 @@ public class MaximumFinder implements ExtendedPlugInFilter, DialogListener {
                 rt.show("Results");
             } else if (outputType==COUNT) {
                 ResultsTable rt = ResultsTable.getResultsTable();
+                if (!IJ.isResultsWindow())
+                	rt = new ResultsTable();
                 rt.incrementCounter();
                 rt.setValue("Count", rt.getCounter()-1, npoints);
                 int measurements = Analyzer.getMeasurements();
diff --git a/ij/plugin/filter/ParticleAnalyzer.java b/ij/plugin/filter/ParticleAnalyzer.java
index 1b7cbeb..e0e6d5b 100644
--- a/ij/plugin/filter/ParticleAnalyzer.java
+++ b/ij/plugin/filter/ParticleAnalyzer.java
@@ -515,9 +515,15 @@ public class ParticleAnalyzer implements PlugInFilter, Measurements {
 			measurements = Analyzer.getMeasurements();
 		measurements &= ~LIMIT;	 // ignore "Limit to Threshold"
 		if (rt==null) {
-			if (!showResults && (WindowManager.getFrame("Results")!=null))
+			Frame table = WindowManager.getFrame("Results");
+			if (!showResults && table!=null) {
 				rt = new ResultsTable();
-			else
+				if (resetCounter && table instanceof TextWindow) {
+					IJ.run("Clear Results");
+					((TextWindow)table).close();
+					rt = Analyzer.getResultsTable();
+				}
+			} else
 				rt = Analyzer.getResultsTable();
 		}
 		analyzer = new Analyzer(imp, measurements, rt);
diff --git a/ij/plugin/filter/PlugInFilterRunner.java b/ij/plugin/filter/PlugInFilterRunner.java
index 07120c4..903820c 100644
--- a/ij/plugin/filter/PlugInFilterRunner.java
+++ b/ij/plugin/filter/PlugInFilterRunner.java
@@ -54,7 +54,8 @@ public class PlugInFilterRunner implements Runnable, DialogListener {
 		if (imp != null) {
 			roi = imp.getRoi();
 			if (roi!=null) roi.endPaste();				// prepare the image: finish previous paste operation (if any)
-			if (!imp.lock()) return;					// exit if image is in use
+			if (!imp.lock())
+				return;					// exit if image is in use
 			nPasses = ((flags&PlugInFilter.CONVERT_TO_FLOAT)!=0) ? imp.getProcessor().getNChannels():1;
 		}
 		if (theFilter instanceof ExtendedPlugInFilter) { // calling showDialog required?
@@ -79,7 +80,8 @@ public class PlugInFilterRunner implements Runnable, DialogListener {
 			}
 		} // if ExtendedPlugInFilter
 		if ((flags&PlugInFilter.DONE)!=0) {
-			if (imp != null) imp.unlock();
+			if (imp != null)
+				imp.unlock();
 			return;
 		} else if (imp==null) {
 			((PlugInFilter)theFilter).run(null);		// not DONE, but NO_IMAGE_REQUIRED
diff --git a/ij/plugin/frame/ContrastAdjuster.java b/ij/plugin/frame/ContrastAdjuster.java
index 3a3bec7..5e0ac53 100644
--- a/ij/plugin/frame/ContrastAdjuster.java
+++ b/ij/plugin/frame/ContrastAdjuster.java
@@ -51,10 +51,12 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 	GridBagConstraints c;
 	int y = 0;
 	boolean windowLevel, balance;
-	Font monoFont = new Font("Monospaced", Font.PLAIN, 12);
+	Font monoFont = new Font("Monospaced", Font.PLAIN, 11);
 	Font sanFont = ImageJ.SansSerif12;
 	int channels = 7; // RGB
 	Choice choice;
+	private String blankMinLabel = "-------";
+	private String blankMaxLabel = "--------";
 
 	public ContrastAdjuster() {
 		super("B&C");
@@ -107,13 +109,17 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 			c.insets = new Insets(0, 10, 0, 10);
 			gridbag.setConstraints(panel, c);
 			panel.setLayout(new BorderLayout());
-			minLabel = new Label("      ", Label.LEFT);
+			minLabel = new Label(blankMinLabel, Label.LEFT);
 			minLabel.setFont(monoFont);
+			if (IJ.debugMode) minLabel.setBackground(Color.yellow);
 			panel.add("West", minLabel);
-			maxLabel = new Label("      " , Label.RIGHT);
+			maxLabel = new Label(blankMaxLabel, Label.RIGHT);
 			maxLabel.setFont(monoFont);
+			if (IJ.debugMode) maxLabel.setBackground(Color.yellow);
 			panel.add("East", maxLabel);
 			add(panel);
+			blankMinLabel = "       ";
+			blankMaxLabel = "        ";
 		}
 
 		// min slider
@@ -419,16 +425,29 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 			if (type!=ImagePlus.GRAY16)
 				realValue = true;
 		}
-		int digits = realValue?2:0;
 		if (windowLevel) {
-			//IJ.log(min+" "+max);
+			int digits = realValue?2:0;
 			double window = max-min;
 			double level = min+(window)/2.0;
 			windowLabel.setText(IJ.d2s(window, digits));
 			levelLabel.setText(IJ.d2s(level, digits));
 		} else {
-			minLabel.setText(IJ.d2s(min, digits));
-			maxLabel.setText(IJ.d2s(max, digits));
+			int digits = realValue?4:0;
+			if (realValue) {
+				double s = min<0||max<0?0.1:1.0;
+				double amin = Math.abs(min);
+				double amax = Math.abs(max);
+				if (amin>99.0*s||amax>99.0*s) digits = 3;
+				if (amin>999.0*s||amax>999.0*s) digits = 2;
+				if (amin>9999.0*s||amax>9999.0*s) digits = 1;
+				if (amin>99999.0*s||amax>99999.0*s) digits = 0;
+				if (amin>9999999.0*s||amax>9999999.0*s) digits = -2;
+			}
+			String minString = IJ.d2s(min, min==0.0?0:digits) + blankMinLabel;
+			minLabel.setText(minString.substring(0,blankMinLabel.length()));
+			String maxString = blankMaxLabel + IJ.d2s(max, digits);
+			maxString = maxString.substring(maxString.length()-blankMaxLabel.length(), maxString.length());
+			maxLabel.setText(maxString);
 		}
 	}
 
@@ -478,8 +497,14 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 	/** Restore image outside non-rectangular roi. */
   	void doMasking(ImagePlus imp, ImageProcessor ip) {
 		ImageProcessor mask = imp.getMask();
-		if (mask!=null)
+		if (mask!=null) {
+			Rectangle r = ip.getRoi();
+			if (mask.getWidth()!=r.width||mask.getHeight()!=r.height) {
+				ip.setRoi(imp.getRoi());
+				mask = ip.getMask();
+			}
 			ip.reset(mask);
+		}
 	}
 
 	void adjustMin(ImagePlus imp, ImageProcessor ip, double minvalue) {
@@ -657,13 +682,13 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 				imp.setSlice(current);
 				option = "stack";
 			} else {
-				if (ip.getMask()!=null) ip.snapshot();
+				ip.snapshot();
 				ip.applyTable(table);
 				ip.reset(ip.getMask());
 				option = "slice";
 			}
 		} else {
-			if (ip.getMask()!=null) ip.snapshot();
+			ip.snapshot();
 			ip.applyTable(table);
 			ip.reset(ip.getMask());
 		}
diff --git a/ij/plugin/frame/Editor.java b/ij/plugin/frame/Editor.java
index b929e93..28aa89c 100644
--- a/ij/plugin/frame/Editor.java
+++ b/ij/plugin/frame/Editor.java
@@ -37,7 +37,7 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		"importPackage(java.util);"+
 		"importPackage(java.io);"+
 		"function print(s) {IJ.log(s);};";
-		
+
 	public static final int MAX_SIZE=28000, XINC=10, YINC=18;
 	public static final int MONOSPACED=1, MENU_BAR=2;
 	public static final int MACROS_MENU_ITEMS = 12;
@@ -85,7 +85,6 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
     private ArrayList undoBuffer = new ArrayList();
     private boolean performingUndo;
     private boolean checkForCurlyQuotes;
-    private boolean useNashorn;
 	
 	public Editor() {
 		this(16, 60, 0, MENU_BAR);
@@ -278,6 +277,10 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 	public void open(String dir, String name) {
 		path = dir+name;
 		File file = new File(path);
+		if (!file.exists()) {
+			IJ.error("File not found: "+path);
+			return;
+		}
 		try {
 			StringBuffer sb = new StringBuffer(5000);
 			BufferedReader r = new BufferedReader(new FileReader(file));
@@ -360,12 +363,14 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			save();
 			String text = ta.getText();
 			if (text.contains("implements PlugInFilter") && text.contains("IJ.run("))
-				IJ.log("Plugins that call IJ.run() should probably implement PlugIn, not PlugInFilter.");
+				IJ.log("<<Plugins that call IJ.run() should probably implement PlugIn, not PlugInFilter.>>");
 			IJ.runPlugIn("ij.plugin.Compiler", path);
 		}
 	}
 	
 	final void runMacro(boolean debug) {
+		if (path!=null)
+			Macro_Runner.setFilePath(path);
 		if (getTitle().endsWith(".js"))
 			{evaluateJavaScript(); return;}
 		else if (getTitle().endsWith(".bsh"))
@@ -414,15 +419,13 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			text = ta.getText();
 		else
 			text = ta.getSelectedText();
-		if (text.equals("")) return;
-		if (useNashorn) {
-			IJ.runPlugIn("ij.plugin.JavaScriptEvaluator", text);
+		if (text.equals(""))
 			return;
-		}
 		text = getJSPrefix("") + text;
-		if ((IJ.isJava16() && !(IJ.isMacOSX()&&!IJ.is64Bit())) && !IJ.isJava18()) {
-			// Use JavaScript engine built into Java 6 and Java 7.
-			// Can't use incompatible Nashorn engine in Java 8 and later.
+		if (IJ.isJava18())
+			text = "load(\"nashorn:mozilla_compat.js\");" + text;
+		if ((IJ.isJava16() && !(IJ.isMacOSX()&&!IJ.is64Bit()))) {
+			// Use JavaScript engine built into Java 6 and later.
 			IJ.runPlugIn("ij.plugin.JavaScriptEvaluator", text);
 		} else {
 			Object js = IJ.runPlugIn("JavaScript", text);
@@ -895,6 +898,7 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 	/** Override windowActivated in PlugInFrame to
 		prevent Mac menu bar from being installed. */
 	public void windowActivated(WindowEvent e) {
+			if (IJ.debugMode) IJ.log("Editor.windowActivated");
 			WindowManager.setWindow(this);
 			instance = this;
 	}
@@ -1268,15 +1272,7 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			IJ.log("debug: "+interp.getLineNumber()+"  "+mode+"  "+interp);
 		if (mode==RUN_TO_COMPLETION)
 			return 0;
-		if (!isVisible()) { // abort macro if user closes window
-			interp.abortMacro();
-			return 0;
-		}
-		toFront();
 		int n = interp.getLineNumber();
-		if (n==previousLine)
-			{previousLine=0; return 0;}
-		previousLine = n;
 		if (mode==RUN_TO_CARET) {
 			if (n==runToLine) {
 				mode = STEP;
@@ -1284,6 +1280,19 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			} else
 				return 0;
 		}
+		if (!isVisible()) { // abort macro if user closes window
+			interp.abortMacro();
+			return 0;
+		}
+		if (n==previousLine) {
+			previousLine=0;
+			return 0;
+		}
+		Window win = WindowManager.getActiveWindow();
+		if (win!=this)
+			IJ.wait(50);
+		toFront();
+		previousLine = n;
 		String text = ta.getText();
 		if (IJ.isWindows())
 			text = text.replaceAll("\r\n", "\n");
diff --git a/ij/plugin/frame/PlugInDialog.java b/ij/plugin/frame/PlugInDialog.java
index b72947e..71d6ed3 100644
--- a/ij/plugin/frame/PlugInDialog.java
+++ b/ij/plugin/frame/PlugInDialog.java
@@ -14,10 +14,8 @@ public class PlugInDialog extends Dialog implements PlugIn, WindowListener, Focu
 		enableEvents(AWTEvent.WINDOW_EVENT_MASK);
 		this.title = title;
 		ImageJ ij = IJ.getInstance();
-		if (IJ.isMacOSX() && ij!=null) {
+		if (IJ.isMacOSX() && ij!=null)
 			ij.toFront(); // needed for keyboard shortcuts to work
-			ij.setMenuBar(Menus.getMenuBar());
-		}
 		addWindowListener(this);
  		addFocusListener(this);
 		if (IJ.isLinux()) setBackground(ImageJ.backgroundColor);
@@ -47,16 +45,10 @@ public class PlugInDialog extends Dialog implements PlugIn, WindowListener, Focu
     }
 
     public void windowActivated(WindowEvent e) {
-		ImageJ ij = IJ.getInstance();
-		if (IJ.isMacOSX() && ij!=null) {
-			IJ.wait(10); // may be needed for Java 1.4 on OS X
-			ij.setMenuBar(Menus.getMenuBar());
-		}
-		WindowManager.setWindow(this);
+        WindowManager.setWindow(this);
 	}
 
 	public void focusGained(FocusEvent e) {
-		//IJ.log("PlugInFrame: focusGained");
 		WindowManager.setWindow(this);
 	}
 
diff --git a/ij/plugin/frame/PlugInFrame.java b/ij/plugin/frame/PlugInFrame.java
index ad494b8..6957e7b 100644
--- a/ij/plugin/frame/PlugInFrame.java
+++ b/ij/plugin/frame/PlugInFrame.java
@@ -43,15 +43,14 @@ public class PlugInFrame extends Frame implements PlugIn, WindowListener, FocusL
     }
 
     public void windowActivated(WindowEvent e) {
-		if (IJ.isMacintosh() && IJ.getInstance()!=null) {
-			IJ.wait(10); // may be needed for Java 1.4 on OS X
-			setMenuBar(Menus.getMenuBar());
+		if (IJ.isMacintosh()) {
+			this.setMenuBar(Menus.getMenuBar());
+			Menus.setMenuBarCount++;
 		}
 		WindowManager.setWindow(this);
 	}
 
 	public void focusGained(FocusEvent e) {
-		//IJ.log("PlugInFrame: focusGained");
 		WindowManager.setWindow(this);
 	}
 
diff --git a/ij/plugin/frame/Recorder.java b/ij/plugin/frame/Recorder.java
index b28753c..79c232d 100644
--- a/ij/plugin/frame/Recorder.java
+++ b/ij/plugin/frame/Recorder.java
@@ -284,8 +284,16 @@ public class Recorder extends PlugInFrame implements PlugIn, ActionListener, Ima
 		recordCall(javaMode()?className+" "+call:call);
 	}
 
+	public static void recordRoi(Roi roi) {
+		if (roi==null)
+			return;
+		Polygon polygon = roi.getPolygon();
+		recordRoi(polygon, roi.getType());
+	}
+
 	public static void recordRoi(Polygon p, int type) {
-		if (textArea==null) return;
+		if (textArea==null)
+			return;
 		if (scriptMode)
 			{recordScriptRoi(p,type); return;}
 		if (type==Roi.ANGLE||type==Roi.POINT) {
@@ -299,7 +307,7 @@ public class Recorder extends PlugInFrame implements PlugIn, ActionListener, Ima
 			String typeStr= type==Roi.ANGLE?"angle":"point";
 			textArea.append("makeSelection(\""+typeStr+"\","+xarr+","+yarr+");\n");
 		} else {
-			String method = type==Roi.POLYGON?"makePolygon":"makeLine";
+			String method = type>=Roi.LINE && type<=Roi.FREELINE?"makeLine":"makePolygon";
 			StringBuffer args = new StringBuffer();
 			for (int i=0; i<p.npoints; i++) {
 				args.append(p.xpoints[i]+",");
@@ -425,7 +433,7 @@ public class Recorder extends PlugInFrame implements PlugIn, ActionListener, Ima
 	public static void saveCommand() {
 		String name = commandName;
 		if (name!=null) {
-			if (commandOptions==null && (name.equals("Fill")||name.equals("Clear")))
+			if (commandOptions==null && (name.equals("Fill")||name.equals("Clear")||name.equals("Draw")))
 				commandOptions = "slice";
 			if (!fgColorSet && (name.equals("Fill")||name.equals("Draw")))
 				setForegroundColor(Toolbar.getForegroundColor());
@@ -801,5 +809,12 @@ public class Recorder extends PlugInFrame implements PlugIn, ActionListener, Ima
 			recordString("setOption(\"BlackBackground\", "+bb+");\n");
 		bbSet = true;
 	}
-
+	
+	/** Override windowActivated in PlugInFrame. */
+	public void windowActivated(WindowEvent e) {
+		if (IJ.isMacintosh() && !IJ.isJava17())
+			this.setMenuBar(Menus.getMenuBar());
+		WindowManager.setWindow(this);
+	}
+	
 }
diff --git a/ij/plugin/frame/RoiManager.java b/ij/plugin/frame/RoiManager.java
index 233b9ba..05504cc 100644
--- a/ij/plugin/frame/RoiManager.java
+++ b/ij/plugin/frame/RoiManager.java
@@ -64,12 +64,14 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	private boolean firstTime = true;
 	private int[] selectedIndexes;
 	private boolean appendResults;
-	private ResultsTable mmResults;
+	private static ResultsTable mmResults;
 	private int imageID;
 	private boolean allowRecording;
 	private boolean recordShowAll = true;
 		
-	/* Constructs and displays an ROIManager. */
+	/** Opens the "ROI Manager" window, or activates it if it is already open.
+	 * @see #getRoiManager
+	*/
 	public RoiManager() {
 		super("ROI Manager");
 		if (instance!=null) {
@@ -218,9 +220,10 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			Point ploc = panel.getLocation();
 			Point bloc = moreButton.getLocation();
 			pm.show(this, ploc.x, bloc.y);
-		} else if (command.equals("OR (Combine)"))
-			combine();
-		else if (command.equals("Split"))
+		} else if (command.equals("OR (Combine)")) {
+			new MacroRunner("roiManager(\"Combine\");");
+			if (Recorder.record) Recorder.record("roiManager", "Combine");
+		} else if (command.equals("Split"))
 			split();
 		else if (command.equals("AND"))
 			and();
@@ -326,6 +329,8 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	}
 
 	boolean addRoi(Roi roi, boolean promptForName, Color color, int lineWidth) {
+		if (listModel==null)
+			IJ.log("<<Error: Uninitialized RoiManager>>");
 		ImagePlus imp = roi==null?getImage():WindowManager.getCurrentImage();
 		if (roi==null) {
 			if (imp==null)
@@ -442,9 +447,12 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	}
 
 	boolean isStandardName(String name) {
-		if (name==null) return false;
-		boolean isStandard = false;
+		if (name==null)
+			return false;
 		int len = name.length();
+		if (len<9 || (len>0&&!Character.isDigit(name.charAt(0))))
+			return false;
+		boolean isStandard = false;
 		if (len>=14 && name.charAt(4)=='-' && name.charAt(9)=='-' )
 			isStandard = true;
 		else if (len>=17 && name.charAt(5)=='-' && name.charAt(11)=='-' )
@@ -469,7 +477,8 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		if (xs.length()>digits) digits = xs.length();
 		String ys = "" + yc;
 		if (ys.length()>digits) digits = ys.length();
-		if (digits==4 && imp!=null && imp.getStackSize()>=10000) digits = 5;
+		if (digits==4 && imp!=null && (imp.getStackSize()>=10000||imp.getHeight()>=10000))
+			digits = 5;
 		xs = "000000" + xc;
 		ys = "000000" + yc;
 		String label = ys.substring(ys.length()-digits) + "-" + xs.substring(xs.length()-digits);
@@ -595,6 +604,14 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		return true;
 	}
 	
+	public void rename(int index, String newName) {
+		if (index<0 || index>=getCount())
+			throw new IllegalArgumentException("Index out of range: "+index);
+		Roi roi = (Roi)rois.get(index);
+		roi.setName(newName);
+		listModel.setElementAt(newName, index);
+	}
+
 	String promptForName(String name) {
 		GenericDialog gd = new GenericDialog("ROI Manager");
 		gd.addStringField("Rename As:", name, 20);
@@ -649,8 +666,10 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 				roi2.setLocation(r1.x+r1.width/2-r2.width/2, r1.y+r1.height/2-r2.height/2);
 			}
 		}
-		if (r.x>=width || r.y>=height || (r.x+r.width)<0 || (r.y+r.height)<0)
-			roi2.setLocation((width-r.width)/2, (height-r.height)/2);
+		if (r.x>=width || r.y>=height || (r.x+r.width)<0 || (r.y+r.height)<0) {
+			if (roi2.getType()!=Roi.POINT)
+				roi2.setLocation((width-r.width)/2, (height-r.height)/2);
+		}
 		if (noUpdateMode) {
 			imp.setRoi(roi2, false);
 			noUpdateMode = false;
@@ -812,13 +831,16 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		DataOutputStream out = null;
 		IJ.showStatus("Saving "+indexes.length+" ROIs "+" to "+path);
 		long t0 = System.currentTimeMillis();
+		String[] names = new String[listModel.size()];
+		for (int i=0; i<listModel.size(); i++)
+			names[i] = (String)listModel.getElementAt(i);
 		try {
 			ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(path)));
 			out = new DataOutputStream(new BufferedOutputStream(zos));
 			RoiEncoder re = new RoiEncoder(out);
 			for (int i=0; i<indexes.length; i++) {
 				IJ.showProgress(i, indexes.length);
-				String label = (String) listModel.getElementAt(indexes[i]);
+				String label = getUniqueName(names, indexes[i]);
 				Roi roi = (Roi)rois.get(indexes[i]);
 				if (IJ.debugMode) IJ.log("saveMultiple: "+i+"  "+label+"  "+roi);
 				if (roi==null) continue;
@@ -843,6 +865,34 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		return true;
 	}
 	
+	String getUniqueName(String[] names, int index) {
+		String name = names[index];
+		int n = 1;
+		int index2 = getIndex(names, index, name);
+		while (index2!=-1) {
+			index2 = getIndex(names, index, name);
+			if (index2!=-1) {
+				int lastDash = name.lastIndexOf("-");
+				if (lastDash!=-1 && name.length()-lastDash<5)
+					name = name.substring(0, lastDash);
+				name = name+"-"+n;
+				n++;
+			}
+			index2 = getIndex(names, index, name);
+		}
+		names[index] = name;
+		return name;
+	}
+    
+	private int getIndex(String[] names, int index, String name) {
+		int index2 = -1;
+		for (int i=0; i<names.length; i++) {
+			if (i!=index && names[i].equals(name))
+			return i;
+		}
+		return index2;
+	}
+
 	private void listRois() {
 		Roi[] list = getRoisAsArray();
 		OverlayCommands.listRois(list);
@@ -887,7 +937,8 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	 * @see <a href="http://imagej.nih.gov/ij/macros/js/MultiMeasureDemo.js">JavaScript example</a>
 	*/
 	public ResultsTable multiMeasure(ImagePlus imp) {
-		ResultsTable rt = multiMeasure(imp, getIndexes(), imp.getStackSize(), false);
+		Roi[] rois = getSelectedRoisAsArray();
+		ResultsTable rt = multiMeasure(imp, rois, false);
 		imp.deleteRoi();
 		return rt;
 	}
@@ -961,12 +1012,18 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			if (nSlices>1)
 				imp.setSlice(currentSlice);
 		} else {
-			ResultsTable rtMulti = multiMeasure(imp, indexes, nSlices, appendResults);
-			mmResults = (ResultsTable)rtMulti.clone();
-			rtMulti.show("Results");
-			imp.setSlice(currentSlice);
-			if (indexes.length>1)
-				IJ.run("Select None");
+			Roi[] rois = getSelectedRoisAsArray();
+			if ("".equals(cmd)) { // run More>>Multi Measure command in separate thread
+				MultiMeasureRunner mmr = new MultiMeasureRunner();
+				mmr.multiMeasure(imp, rois, appendResults);
+			} else {
+				ResultsTable rtMulti = multiMeasure(imp, rois, appendResults);
+				mmResults = (ResultsTable)rtMulti.clone();
+				rtMulti.show("Results");
+				imp.setSlice(currentSlice);
+				if (indexes.length>1)
+					IJ.run("Select None");
+			}
 		}
 		if (record()) {
 			if (Recorder.scriptMode()) {
@@ -990,7 +1047,8 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		return true;
 	}
 	
-	private ResultsTable multiMeasure(ImagePlus imp, int[] indexes, int nSlices, boolean appendResults) {
+	private static ResultsTable multiMeasure(ImagePlus imp, Roi[] rois, boolean appendResults) {
+		int nSlices = imp.getStackSize();
 		Analyzer aSys = new Analyzer(imp); // System Analyzer
 		ResultsTable rtSys = Analyzer.getResultsTable();
 		ResultsTable rtMulti = new ResultsTable();
@@ -1006,26 +1064,25 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			if ((Analyzer.getMeasurements()&Measurements.LABELS)!=0)
 				rtMulti.addLabel("Label", imp.getTitle());
 			int roiIndex = 0;
-			for (int i=0; i<indexes.length; i++) {
-				if (restoreWithoutUpdate(imp, indexes[i])) {
-					roiIndex++;
-					aSys.measure();
-					for (int j=0; j<=rtSys.getLastColumn(); j++){
-						float[] col = rtSys.getColumn(j);
-						String head = rtSys.getColumnHeading(j);
-						String suffix = ""+roiIndex;
-						Roi roi = imp.getRoi();
-						if (roi!=null) {
-							String name = roi.getName();
-							if (name!=null && name.length()>0 && (name.length()<9||!Character.isDigit(name.charAt(0))))
-								suffix = "("+name+")";
-						}
-						if (head!=null && col!=null && !head.equals("Slice"))
-							rtMulti.addValue(head+suffix, rtSys.getValue(j,rtSys.getCounter()-1));
+			for (int i=0; i<rois.length; i++) {
+				imp.setRoi(rois[i]);
+				roiIndex++;
+				aSys.measure();
+				for (int j=0; j<=rtSys.getLastColumn(); j++){
+					float[] col = rtSys.getColumn(j);
+					String head = rtSys.getColumnHeading(j);
+					String suffix = ""+roiIndex;
+					Roi roi = imp.getRoi();
+					if (roi!=null) {
+						String name = roi.getName();
+						if (name!=null && name.length()>0 && (name.length()<9||!Character.isDigit(name.charAt(0))))
+							suffix = "("+name+")";
 					}
-				} else
-					break;
+					if (head!=null && col!=null && !head.equals("Slice"))
+						rtMulti.addValue(head+suffix, rtSys.getValue(j,rtSys.getCounter()-1));
+				}
 			}
+			if (nSlices>1) IJ.showProgress(slice,nSlices);
 		}
 		return rtMulti;
 	}
@@ -1283,36 +1340,39 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		return labelsCheckbox.getState();
 	}
 
-	void combine() {
+	private void combine() {
 		ImagePlus imp = getImage();
-		if (imp==null) return;
-		int[] indexes = getSelectedIndexes();
-		if (indexes.length==1) {
+		if (imp==null)
+			return;
+		Roi[] rois = getSelectedRoisAsArray();
+		if (rois.length==1) {
 			error("More than one item must be selected, or none");
 			return;
 		}
-		if (indexes.length==0)
-			indexes = getAllIndexes();
 		int nPointRois = 0;
-		for (int i=0; i<indexes.length; i++) {
-			Roi roi = (Roi)rois.get(indexes[i]);
-			if (roi.getType()==Roi.POINT)
+		for (int i=0; i<rois.length; i++) {
+			if (rois[i].getType()==Roi.POINT)
 				nPointRois++;
 			else
 				break;
 		}
-		if (nPointRois==indexes.length)
-			combinePoints(imp, indexes);
+		if (nPointRois==rois.length)
+			combinePoints(imp, rois);
 		else
-			combineRois(imp, indexes);
-		if (record()) Recorder.record("roiManager", "Combine");
+			combineRois(imp, rois);
 	}
 	
-	void combineRois(ImagePlus imp, int[] indexes) {
+	private void combineRois(ImagePlus imp, Roi[] rois) {
+		IJ.resetEscape();
 		ShapeRoi s1=null, s2=null;
 		ImageProcessor ip = null;
-		for (int i=0; i<indexes.length; i++) {
-			Roi roi = (Roi)rois.get(indexes[i]);
+		for (int i=0; i<rois.length; i++) {
+			IJ.showProgress(i, rois.length-1);
+			if (IJ.escapePressed()) {
+				IJ.showProgress(1.0);
+				return;
+			}
+			Roi roi = rois[i];
 			if (!roi.isArea()) {
 				if (ip==null)
 					ip = new ByteProcessor(imp.getWidth(), imp.getHeight());
@@ -1353,16 +1413,16 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		return tts.convert(ip);
 	}
 
-	void combinePoints(ImagePlus imp, int[] indexes) {
-		int n = indexes.length;
+	void combinePoints(ImagePlus imp, Roi[] rois) {
+		int n = rois.length;
 		Polygon[] p = new Polygon[n];
 		int points = 0;
 		for (int i=0; i<n; i++) {
-			Roi roi = (Roi)rois.get(indexes[i]);
-			p[i] = roi.getPolygon();
+			p[i] = rois[i].getPolygon();
 			points += p[i].npoints;
 		}
-		if (points==0) return;
+		if (points==0)
+			return;
 		int[] xpoints = new int[points];
 		int[] ypoints = new int[points];
 		int index = 0;
@@ -1482,10 +1542,18 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		boolean removeChannels = position==CHANNEL;
 		boolean removeFrames = position==FRAME;
 		boolean removeSlices = !(removeChannels||removeFrames);
+		ImagePlus imp = WindowManager.getCurrentImage();
 		if (position==SHOW_DIALOG) {
-			ImagePlus imp = WindowManager.getCurrentImage();
-			if (imp!=null && !imp.isHyperStack())
-				{channel=false; slice=true; frame=false;}
+			if (imp!=null && !imp.isHyperStack()) {
+				channel=false; slice=true; frame=false;
+			}
+			if (imp!=null && imp.isHyperStack()) {
+				channel = slice = frame = false;
+				if (imp.getNSlices()>1)
+					slice = true;
+				if (imp.getNFrames()>1 && imp.getNSlices()==1)
+					frame = true;
+			}
 			Font font = new Font("SansSerif", Font.BOLD, 12);
 			GenericDialog gd = new GenericDialog("Remove");
 			gd.setInsets(5,15,0);
@@ -1502,9 +1570,6 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			removeChannels = gd.getNextBoolean();
 			removeSlices = gd.getNextBoolean();
 			removeFrames = gd.getNextBoolean();
-			channel = removeChannels;
-			slice = removeSlices;
-			frame = removeFrames;
 		}
 		if (!removeChannels && !removeSlices && !removeFrames) {
 			slice = true;
@@ -1513,6 +1578,14 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		for (int i=0; i<indexes.length; i++) {
 			int index = indexes[i];
 			Roi roi = (Roi)rois.get(index);
+			String name = (String)listModel.getElementAt(index);
+			int n = getSliceNumber(name);
+			if (n>0) {
+				String name2 = name.substring(5, name.length());
+				roi.setName(name2);
+				rois.set(index, roi);
+				listModel.setElementAt(name2, index);
+			}
 			int c = roi.getCPosition();
 			int z = roi.getZPosition();
 			int t = roi.getTPosition();
@@ -1521,19 +1594,11 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 				if (removeSlices) z = 0;
 				if (removeFrames) t = 0;
 				roi.setPosition(c, z, t);
-				continue;
-			}
-			String name = (String) listModel.getElementAt(index);
-			int n = getSliceNumber(name);
-			if (n==-1) {
+			} else
 				roi.setPosition(0);
-				continue;
-			}
-			String name2 = name.substring(5, name.length());
-			roi.setName(name2);
-			rois.set(index, roi);
-			listModel.setElementAt(name2, index);
 		}
+		if (imp!=null)
+			imp.draw();
 		if (record()) {
 			if (removeChannels) Recorder.record("roiManager", "Remove Channel Info");
 			if (removeSlices) Recorder.record("roiManager", "Remove Slice Info");
@@ -1701,8 +1766,18 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			ignoreInterrupts = false;
 	}
 	
-	/** Returns a reference to the ROI Manager
-		or null if it is not open. */
+	/** Returns a reference to the ROI Manager and opens
+		 the "ROI Manager" window if it is not already open. */
+	public static RoiManager getRoiManager() {
+		if (instance!=null)
+			return (RoiManager)instance;
+		else
+			return new RoiManager();
+	}
+
+	/** Returns a reference to the ROI Manager, or null if it is not open.
+	 * @see #getRoiManager
+	*/
 	public static RoiManager getInstance() {
 		return (RoiManager)instance;
 	}
@@ -1716,15 +1791,17 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		return rm;
 	}
 
-
 	/** Obsolete
 	 * @deprecated
 	 * @see #getCount
 	 * @see #getRoisAsArray
 	*/
 	public Hashtable getROIs() {
-		//return rois;
-		return null;
+		Roi[] rois = getRoisAsArray();
+		Hashtable ht = new Hashtable();
+		for (int i=0; i<rois.length; i++)
+			ht.put((String)listModel.getElementAt(i), rois[i]);
+		return ht;
 	}
 
 	/** Obsolete
@@ -2145,6 +2222,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	public void close() {
 		super.close();
 		instance = null;
+		mmResults = null;
 		Prefs.saveLocation(LOC_KEY, getLocation());
 		if (!showAllCheckbox.getState() || IJ.macroRunning())
 			return;
@@ -2294,7 +2372,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	public void allowRecording(boolean allow) {
 		this.allowRecording = allow;
 	}
-
+	
 	public void mouseReleased (MouseEvent e) {}
 	public void mouseClicked (MouseEvent e) {}
 	public void mouseEntered (MouseEvent e) {}
@@ -2358,6 +2436,34 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
     		}
     	}
 	}
+	
+	// This class runs the "Multi Measure" command in a separate thread
+	private class MultiMeasureRunner implements Runnable  {
+		private Thread thread;
+		private ImagePlus imp;
+		private Roi[] rois;
+		private boolean appendResults;
+		
+		public void multiMeasure(ImagePlus imp, Roi[] rois, boolean appendResults) {
+			this.imp = imp;
+			this.rois = rois;
+			this.appendResults = appendResults;
+			thread = new Thread(this, "MultiMeasure"); 
+			thread.start();
+		}
+	
+		public void run() {
+			int currentSlice = imp.getCurrentSlice();
+			ResultsTable rtMulti = RoiManager.multiMeasure(imp, rois, appendResults);
+			mmResults = (ResultsTable)rtMulti.clone();
+			rtMulti.show("Results");
+			imp.setSlice(currentSlice);
+			if (rois.length>1)
+				IJ.run("Select None");
+		}
+		
+	}
+
 
 }
 
diff --git a/ij/plugin/frame/ThresholdAdjuster.java b/ij/plugin/frame/ThresholdAdjuster.java
index 557c75d..2e71821 100644
--- a/ij/plugin/frame/ThresholdAdjuster.java
+++ b/ij/plugin/frame/ThresholdAdjuster.java
@@ -66,7 +66,14 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 		super("Threshold");
 		ImagePlus cimp = WindowManager.getCurrentImage();
 		if (cimp!=null && cimp.getBitDepth()==24) {
-			IJ.run(cimp, "Color Threshold...", "");
+			IJ.error("Threshold Adjuster",
+				"Image>Adjust>Threshold only works with grayscale images.\n"
+				+"What you can do:\n"
+				+"   Image>Type>8-bit (convert to grayscale)\n"
+				+"   Image>Type>RGB Stack (convert to RGB stack)\n"
+				+"   Image>Type>HSB Stack (convert to HSB stack)\n"
+				+"   Image>Color>Split Channels (convert to 3 grayscale images)\n"
+				+"   Image>Adjust>Color Threshold (do color thresholding)\n");
 			return;
 		}
 		if (instance!=null) {
@@ -294,6 +301,12 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 			mode = modeChoice.getSelectedIndex();
 			setLutColor(mode);
 			doStateChange = true;
+			if (Recorder.record) {
+				if (Recorder.scriptMode())
+					Recorder.recordCall("ThresholdAdjuster.setMode(\""+modes[mode]+"\");");
+				else
+					Recorder.recordString("call(\"ij.plugin.frame.ThresholdAdjuster.setMode\", \""+modes[mode]+"\");\n");
+			}
 		} else
 			doAutoAdjust = true;
 		notify();
@@ -835,6 +848,27 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 	public static String getMode() {
 		return modes[mode];
 	}
+	
+	/** Sets the current mode ("Red","B&W" or"Over/Under"). */
+	public static void setMode(String tmode) {
+		if (instance!=null) synchronized (instance) {
+			ThresholdAdjuster ta = ((ThresholdAdjuster)instance);
+			if (modes[0].equals(tmode))
+				mode = 0;
+			else if (modes[1].equals(tmode))
+				mode = 1;
+			else if (modes[2].equals(tmode))
+				mode = 2;
+			else
+				return;
+			ta.setLutColor(mode);
+			ta.doStateChange = true;
+			ta.modeChoice.select(mode);
+			ta.notify();
+		}
+	}
+
+
 
 } // ThresholdAdjuster class
 
diff --git a/ij/plugin/tool/PixelInspectionTool.java b/ij/plugin/tool/PixelInspectionTool.java
index 14ebe18..96f48bb 100644
--- a/ij/plugin/tool/PixelInspectionTool.java
+++ b/ij/plugin/tool/PixelInspectionTool.java
@@ -444,6 +444,8 @@ class PixelInspector extends PlugInFrame
 			double[] minmax = Tools.getMinMax(data);
 			double maxDataValue = Math.max(Math.abs(minmax[0]), Math.abs(minmax[1]));
 			digits = (int)(6-Math.log(maxDataValue)/Math.log(10));
+			if (maxDataValue==0.0)
+				digits = 6;
 			expMode = digits<-1 || digits>7;
 			if (Math.min(minmax[0], minmax[1]) < 0)
 				digits--; //more space needed for minus sign
diff --git a/ij/plugin/tool/RoiRotationTool.java b/ij/plugin/tool/RoiRotationTool.java
index 0d54002..7f00873 100644
--- a/ij/plugin/tool/RoiRotationTool.java
+++ b/ij/plugin/tool/RoiRotationTool.java
@@ -1,4 +1,3 @@
-
 /* 
  * This plugin implements the "Selection Rotator" tool, which 
  * can be used to interactively rotate selections.
@@ -6,15 +5,13 @@
  * @author: 	Peter Haub, Oct. 2015, phaub at dipsystems.de
  */
 
-
 package ij.plugin.tool;
 import ij.*;
 import ij.gui.*;
 import ij.plugin.RoiRotator;
 import ij.plugin.tool.PlugInTool;
-
-import java.awt.Event;
-import java.awt.Rectangle;
+import ij.plugin.frame.Recorder;
+import java.awt.*;
 import java.awt.event.*;
 
 public class RoiRotationTool extends PlugInTool {
@@ -48,11 +45,10 @@ public class RoiRotationTool extends PlugInTool {
 		if (defaultRotationMode == UPDOWNROTATION){
 			centerX = imp.getWidth()/2;
 			centerY = imp.getHeight()/2;
-		}
-		else{
-			Rectangle bounds = roi.getBounds();
-			centerX = bounds.x + bounds.width/2;
-			centerY = bounds.y + bounds.height/2;
+		} else {
+			double[] centroid = roi.getContourCentroid();
+			centerX = (int)Math.round(centroid[0]);
+			centerY = (int)Math.round(centroid[1]);
 		}
 	}
 	
@@ -70,12 +66,23 @@ public class RoiRotationTool extends PlugInTool {
 			rotateRoi(e.getX(), e.getY());
 	}
 		
+	public void mouseReleased(ImagePlus imp, MouseEvent e) {
+		if (Recorder.record) {
+			Roi roi = imp.getRoi();
+			int n = roi.getPolygon().npoints;
+			if (n<=20 && roi.getType()!=Roi.LINE)
+				Recorder.recordRoi(roi);
+			else if (n>20)
+				Recorder.recordString("// Selection has "+n+" points, too many to record.\n");
+		}
+	}
+
 	public void showOptionsDialog() {
 		IJ.log("PlugInTool MouseRoiRotator Peter Haub dipsystems.de 10'2015");
 	}
 
 	public String getToolName() {
-		return "Selection Rotator (press alt key to move)";
+		return "Selection Rotator (press alt or shift to move)";
 	}
 	
 	public String getToolIcon() {
@@ -134,6 +141,6 @@ public class RoiRotationTool extends PlugInTool {
 		
 		imp2.draw();
 	}
-
+	
 }
 
diff --git a/ij/process/AutoThresholder.java b/ij/process/AutoThresholder.java
index 527ace2..8e35d96 100644
--- a/ij/process/AutoThresholder.java
+++ b/ij/process/AutoThresholder.java
@@ -2,8 +2,8 @@ package ij.process;
 import ij.IJ;
 import java.util.Arrays;
 
-/** Autothresholding methods from the Auto_Threshold plugin (http://pacific.mpi-cbg.de/wiki/index.php/Auto_Threshold)
-    by G.Landini at bham dot ac dot uk. */
+/** Autothresholding methods (limited to 256 bin histograms) from the Auto_Threshold plugin 
+    (http://fiji.sc/Auto_Threshold) by G.Landini at bham dot ac dot uk). */
 public class AutoThresholder {
 	private static String[] mStrings;
 			
@@ -37,7 +37,11 @@ public class AutoThresholder {
 		return mStrings;
 	}
 	
+	/** Calculates and returns a threshold using the specified
+		method and 256 bin histogram. */
 	public int getThreshold(Method method, int[] histogram) {
+		if (histogram.length!=256)
+			throw new IllegalArgumentException("Histogram length not 256");
 		int threshold = 0;
 		switch (method) {
 			case Default: threshold =  defaultIsoData(histogram); break;
diff --git a/ij/process/ByteProcessor.java b/ij/process/ByteProcessor.java
index e2dc50e..462e27f 100644
--- a/ij/process/ByteProcessor.java
+++ b/ij/process/ByteProcessor.java
@@ -184,9 +184,8 @@ public class ByteProcessor extends ImageProcessor {
 	
 	/** Reset the image from snapshot.*/
 	public void reset() {
-		if (snapshotPixels==null)
-			return;	
-        System.arraycopy(snapshotPixels,0,pixels,0,width*height);
+		if (snapshotPixels!=null)
+			System.arraycopy(snapshotPixels,0,pixels,0,width*height);
 	}
 	
 	/** Swaps the pixel and snapshot (undo) arrays. */
@@ -937,8 +936,6 @@ public class ByteProcessor extends ImageProcessor {
 		double yFraction = y - ybase;
 		int offset = ybase * width + xbase;
 		int lowerLeft = pixels[offset]&255;
-		//if ((xbase>=(width-1))||(ybase>=(height-1)))
-		//	return lowerLeft;
 		int lowerRight = pixels[offset + 1]&255;
 		int upperRight = pixels[offset + width + 1]&255;
 		int upperLeft = pixels[offset + width]&255;
@@ -962,8 +959,8 @@ public class ByteProcessor extends ImageProcessor {
 		double xScale = (double)dstWidth/roiWidth;
 		double yScale = (double)dstHeight/roiHeight;
 		if (interpolationMethod!=NONE) {
-			dstCenterX += xScale/2.0;
-			dstCenterY += yScale/2.0;
+			if (dstWidth!=width) dstCenterX+=xScale/4.0;
+			if (dstHeight!=height) dstCenterY+=yScale/4.0;
 		}
 		ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
 		byte[] pixels2 = (byte[])ip2.getPixels();
diff --git a/ij/process/ColorProcessor.java b/ij/process/ColorProcessor.java
index 2bb7803..456fc97 100644
--- a/ij/process/ColorProcessor.java
+++ b/ij/process/ColorProcessor.java
@@ -202,9 +202,8 @@ public class ColorProcessor extends ImageProcessor {
 
 
 	public void reset() {
-		if (snapshotPixels==null)
-			return;
-		System.arraycopy(snapshotPixels, 0, pixels, 0, width*height);
+		if (snapshotPixels!=null)
+			System.arraycopy(snapshotPixels, 0, pixels, 0, width*height);
 	}
 
 
@@ -945,8 +944,8 @@ public class ColorProcessor extends ImageProcessor {
 		double xlimit = width-1.0, xlimit2 = width-1.001;
 		double ylimit = height-1.0, ylimit2 = height-1.001;
 		if (interpolationMethod==BILINEAR) {
-			dstCenterX += xScale/2.0;
-			dstCenterY += yScale/2.0;
+			if (dstWidth!=width) dstCenterX+=xScale/4.0;
+			if (dstHeight!=height) dstCenterY+=yScale/4.0;
 		}
 		ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
 		int[] pixels2 = (int[])ip2.getPixels();
diff --git a/ij/process/FloatProcessor.java b/ij/process/FloatProcessor.java
index 2959129..a300229 100644
--- a/ij/process/FloatProcessor.java
+++ b/ij/process/FloatProcessor.java
@@ -869,8 +869,8 @@ public class FloatProcessor extends ImageProcessor {
 		double xScale = (double)dstWidth/roiWidth;
 		double yScale = (double)dstHeight/roiHeight;
 		if (interpolationMethod!=NONE) {
-			dstCenterX += xScale/2.0;
-			dstCenterY += yScale/2.0;
+			if (dstWidth!=width) dstCenterX+=xScale/4.0;
+			if (dstHeight!=height) dstCenterY+=yScale/4.0;
 		}
 		int inc = getProgressIncrement(dstWidth,dstHeight);
 		ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
diff --git a/ij/process/ImageProcessor.java b/ij/process/ImageProcessor.java
index 685bdfd..344b897 100644
--- a/ij/process/ImageProcessor.java
+++ b/ij/process/ImageProcessor.java
@@ -1118,6 +1118,31 @@ public abstract class ImageProcessor implements Cloneable {
 		lineTo(x2, y2);
 	}
 
+	/* Draws a line using the Bresenham's algorithm that is 4-connected instead of 8-connected.<br>
+		Based on code from http://stackoverflow.com/questions/5186939/algorithm-for-drawing-a-4-connected-line<br>
+		Author: Gabriel Landini (G.Landini at bham.ac.uk)
+	*/
+	 public void drawLine4(int x1, int y1, int x2, int y2) {
+		int dx = Math.abs(x2 - x1);
+		int dy = Math.abs(y2 - y1);
+		int sgnX = x1 < x2 ? 1 : -1;
+		int sgnY = y1 < y2 ? 1 : -1;
+		int e = 0;
+		for (int i=0; i < dx+dy; i++) {
+			putPixel(x1, y1, fgColor);
+			int e1 = e + dy;
+			int e2 = e - dx;
+			if (Math.abs(e1) < Math.abs(e2)) {
+				x1 += sgnX;
+				e = e1;
+			} else {
+				y1 += sgnY;
+				e = e2;
+			}
+		}
+		putPixel(x2, y2, fgColor);
+	}
+
 	/** Draws a rectangle. */
 	public void drawRect(int x, int y, int width, int height) {
 		if (width<1 || height<1)
@@ -1729,7 +1754,7 @@ public abstract class ImageProcessor implements Cloneable {
 	/** Uses the current interpolation method (bilinear or bicubic)
 		to find the pixel value at real coordinates (x,y). */
 	public abstract double getInterpolatedPixel(double x, double y);
-
+	
 	/** Uses the current interpolation method to find the pixel value at real coordinates (x,y).
 		For RGB images, the argb values are packed in an int. For float images,
 		the value must be converted using Float.intBitsToFloat().  Returns zero
@@ -1805,19 +1830,6 @@ public abstract class ImageProcessor implements Cloneable {
 		return z;
 	}	
 
-	/*
-		// a = 0.5
-	double cubic2(double x) {
-		if (x < 0) x = -x;
-		double z = 0;
-		if (x < 1)
-			z = 1.5*x*x*x + -2.5*x*x + 1.0;
-		else if (x < 2)
-			z = -0.5*x*x*x + 2.5*x*x - 4.0*x + 2.0;
-		return z;
-	}
-	*/	
-
 	private final double getInterpolatedEdgeValue(double x, double y) {
 		int xbase = (int)x;
 		int ybase = (int)y;
@@ -2276,7 +2288,7 @@ public abstract class ImageProcessor implements Cloneable {
 		threshold(getAutoThreshold());
 	}
 
-	/**	Returns a pixel value (threshold) that can be used to divide the image into objects 
+	/** Returns a pixel value (threshold) that can be used to divide the image into objects 
 		and background. It does this by taking a test threshold and computing the average 
 		of the pixels at or below the threshold and pixels above. It then computes the average
 		of those two, increments the threshold, and repeats the process. Incrementing stops 
@@ -2531,11 +2543,10 @@ public abstract class ImageProcessor implements Cloneable {
 	
 	/** Blurs the image by convolving with a Gaussian function. */
 	public void blurGaussian(double sigma) {
-		double accuracy = getBitDepth()==8||getBitDepth()==24?0.002:0.0002;
 		resetRoi();
 		GaussianBlur gb = new GaussianBlur();
 		gb.showProgress(false);
-        gb.blurGaussian(this, sigma, sigma, accuracy);
+        gb.blurGaussian(this, sigma);
 	}
 
 	/** Uses the Process/Math/Macro command to apply macro code to this image. */
diff --git a/ij/process/ShortProcessor.java b/ij/process/ShortProcessor.java
index 0841356..4cba2c7 100644
--- a/ij/process/ShortProcessor.java
+++ b/ij/process/ShortProcessor.java
@@ -863,8 +863,8 @@ public class ShortProcessor extends ImageProcessor {
 		double xScale = (double)dstWidth/roiWidth;
 		double yScale = (double)dstHeight/roiHeight;
 		if (interpolationMethod!=NONE) {
-			dstCenterX += xScale/2.0;
-			dstCenterY += yScale/2.0;
+			if (dstWidth!=width) dstCenterX+=xScale/4.0;
+			if (dstHeight!=height) dstCenterY+=yScale/4.0;
 		}
 		int inc = getProgressIncrement(dstWidth,dstHeight);
 		ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
diff --git a/ij/process/StackProcessor.java b/ij/process/StackProcessor.java
index dbfd8b7..fa5de3d 100644
--- a/ij/process/StackProcessor.java
+++ b/ij/process/StackProcessor.java
@@ -110,6 +110,7 @@ public class StackProcessor {
 	public ImageStack resize(int newWidth, int newHeight, boolean averageWhenDownsizing) {
 	    ImageStack stack2 = new ImageStack(newWidth, newHeight);
  		ImageProcessor ip2;
+ 		Rectangle roi = ip!=null?ip.getRoi():null;
     	if (ip==null)
     		ip = stack.getProcessor(1).duplicate();
 		try {
diff --git a/release-notes.html b/release-notes.html
index 8c9abe5..f8dd615 100644
--- a/release-notes.html
+++ b/release-notes.html
@@ -5,47 +5,26 @@
 </head>
 <body>
 
-<li> <u>1.50d 25 October 2015</u>
+<li> <u>1.50i 25 March 2016</u>
 <ul>
-<li> Thanks to Peter Haub, added the <i>Selection Rotator</i> tool
-to the Toolbar's ">>" menu. To move the selection, hold down
-the alt or shift key.
-<li> The <i>Edit>Options>Point Tool</i> dialog,
- in multi-point mode, is now non-modal and has a "Counter"
-drop down menu. As a shortcut, double click on the multi-point
-tool icon to open this dialog.
-<li> A point in a polygon, polyline or point selection can now be
-deleted by either alt-clicking or control-clicking on it.
-<li> The <i>File>Import>Raw</i> command now
-uses "smart recording" (default options are not recorded).
-<li> Added the <i>Help>Examples>Autorun</i> checkbox menu item.
-<li> Thanks to Michael Schmid, the AVI Reader now opens most oversized (>4GB)
-ImageJ AVI 1 files and the AVI Writer uses the AVI 2 format, which is AVI 1 
-compatible for files up to approx 0.9 GB in size. Use the 
-<a href="plugins/virtual-test-stack/index.html">Virtual Test Stack</a>
-plugin to create multi-gigabyte test stacks.
-<li> Thanks to Bill Christens-Barry, added the WaitForUserDialog.setNextLocation(x,y) method
-(<a href="macros/examples/SetWaitForUserLocation">macro example</a>).
-<li> Added the add(Roi,name) and remove(name) methods to the Overlay class.
-<li> Added the ThresholdAdjuster.setMethod() method.
-<li> Thanks to Kota Miura, worked around Java 8 bugs that caused cut, copy and paste
-to not work in the curve fitter (<i>Analyze>Tools>Curve Fitting</i>).
-<li> Thanks to Jan Eglinger, fixed a bug that caused the <i>Image>Scale</i> 
-command to ignore the "z=" option when run from a macro.
-<li> Thanks to Stein Rorvik, fixed a bug that caused calibration bars
-(<i>Analyze>Tools>Calibration Bar</i>) to remove existing overlays.
-<li> Fixed a bug that caused the <i>Process>Math>Macro</i>
-command to not work correctly with 16-bit and 32-bit images if the
-macro code called the getPixel() function.
-<li> Fixed a bug that caused the <i>Rotate 90 Degrees Right</i> and
-<i>Rotate 90 Degrees Left</i> commands to not correctly handle
-non-zero origins.
-<li> Fixed a bug that caused polygon and polyline selections to lose properties
-like stroke color and stroke width after deleting one of the selection's
-points by alt-clicking on it.
-<li> Thanks to Vytas Bindokas, fixed a bug that caused the waitForUser() macro
-function to fail on Windows.
-
+<li> The ImageJ website
+(<a href="http://imagej.nih.gov/ij">imagej.nih.gov/ij</a>)
+will soon start using encryption incompatible
+with Java so sample images and updates are now downloaded from
+<a href="http://wsr.imagej.net">wsr.imagej.net</a>
+and URLs used to open images and text files 
+are redirected to
+<a href="http://mirror.imagej.net">mirror.imagej.net</a>.
+<li> Thanks to Thorsten Wagner, added the is("global calibration") macro function.
+<li> Thanks to Philippe Carl, added the RoiManager.rename(index,name) method.
+<li> Thanks to Nicolas Cedilnik and Mark Hiner, fixed a bug that could cause an
+exception when saving a virtual stack.
+<li> Thanks to Kees Straatman, fixed a bug that caused the ROI Manager's "Multi Measure"
+command to sometimes not work as expected when the "append" option was used.
+<li> Fixed a bug that caused the "Decimal Places" field in the <i>Analyze>Set Measurements</i>
+dialog to be ignored after the value was set to zero.
+<li> Thanks to Stein Rorvik, fixed a 1.50d regression that caused duplicated images
+to lose their density calibration.
 </ul>
 
 <a href="http://imagej.nih.gov/ij">Home</a>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/imagej.git



More information about the debian-med-commit mailing list