[med-svn] [imagej] 02/04: New upstream version 1.51p+dfsg

Andreas Tille tille at debian.org
Fri Aug 11 22:29:52 UTC 2017


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

tille pushed a commit to branch master
in repository imagej.

commit a544c43baeb49013118e5ccf2aae7dc4a34e590a
Author: Andreas Tille <tille at debian.org>
Date:   Sat Aug 12 00:28:00 2017 +0200

    New upstream version 1.51p+dfsg
---
 IJ_Props.txt                           |  15 +--
 ij/CompositeImage.java                 |   2 +-
 ij/Executer.java                       |   2 +-
 ij/IJ.java                             |  10 +-
 ij/ImageJ.java                         |  29 ++++-
 ij/ImagePlus.java                      |  45 +++++---
 ij/Menus.java                          |  19 +++-
 ij/Prefs.java                          |  22 +++-
 ij/VirtualStack.java                   |  36 ++++---
 ij/gui/EllipseRoi.java                 |  48 ++++-----
 ij/gui/GUI.java                        |   3 -
 ij/gui/GenericDialog.java              |  70 +++++-------
 ij/gui/HistogramWindow.java            | 112 +++++++++++--------
 ij/gui/ImageCanvas.java                |  12 ++-
 ij/gui/ImageWindow.java                |  10 +-
 ij/gui/Line.java                       |  89 +++++++++++++++
 ij/gui/NewImage.java                   |  16 ++-
 ij/gui/Overlay.java                    |  17 +++
 ij/gui/Plot.java                       |   2 +-
 ij/gui/PlotWindow.java                 |   2 +-
 ij/gui/PointRoi.java                   |  10 +-
 ij/gui/PolygonRoi.java                 |   7 +-
 ij/gui/ProgressBar.java                |   3 +
 ij/gui/Roi.java                        | 114 ++++++++++----------
 ij/gui/RoiProperties.java              |   8 +-
 ij/gui/RotatedRectRoi.java             | 176 ++++++++++++++++++++++++++++++
 ij/gui/ScrollbarWithLabel.java         |   1 -
 ij/gui/Toolbar.java                    | 140 +++++++++++++++---------
 ij/gui/YesNoCancelDialog.java          |   8 +-
 ij/io/FileOpener.java                  |   4 +-
 ij/io/ImageReader.java                 |   3 +-
 ij/io/Opener.java                      |   9 +-
 ij/io/RoiDecoder.java                  |  12 ++-
 ij/io/RoiEncoder.java                  |  16 ++-
 ij/io/TiffDecoder.java                 |   5 +-
 ij/macro/Functions.java                | 191 +++++++++++++++++++++++++++------
 ij/macro/Interpreter.java              |  18 +++-
 ij/measure/ResultsTable.java           |   7 +-
 ij/plugin/AVI_Reader.java              | 128 ++++++++++++----------
 ij/plugin/BatchProcessor.java          |  39 +++++--
 ij/plugin/Binner.java                  |   2 +-
 ij/plugin/CalibrationBar.java          |   5 +-
 ij/plugin/ChannelArranger.java         |   6 +-
 ij/plugin/Colors.java                  |   2 +-
 ij/plugin/Converter.java               |   8 +-
 ij/plugin/Duplicator.java              |   5 +
 ij/plugin/FileInfoVirtualStack.java    |   4 +-
 ij/plugin/FolderOpener.java            |   6 ++
 ij/plugin/Grid.java                    |  33 +++++-
 ij/plugin/GroupedZProjector.java       |  16 ++-
 ij/plugin/HyperStackConverter.java     |   2 +-
 ij/plugin/ImageInfo.java               |  15 +++
 ij/plugin/LutLoader.java               |   5 +-
 ij/plugin/Macro_Runner.java            |   4 +-
 ij/plugin/Options.java                 |   9 +-
 ij/plugin/OverlayCommands.java         |   1 -
 ij/plugin/PNM_Writer.java              |  39 ++++---
 ij/plugin/PointToolOptions.java        |  28 +++--
 ij/plugin/Profiler.java                |   8 +-
 ij/plugin/Projector.java               |   7 +-
 ij/plugin/RGBStackMerge.java           |   2 +
 ij/plugin/Resizer.java                 |   8 +-
 ij/plugin/RoiRotator.java              |   2 -
 ij/plugin/Scaler.java                  |   5 +
 ij/plugin/ScreenGrabber.java           |  21 ++++
 ij/plugin/Selection.java               |   2 +-
 ij/plugin/SimpleCommands.java          |  11 ++
 ij/plugin/SubstackMaker.java           |  10 ++
 ij/plugin/ZProjector.java              |   2 +-
 ij/plugin/Zoom.java                    |   8 +-
 ij/plugin/filter/Analyzer.java         |  33 ++++--
 ij/plugin/filter/ImageMath.java        |   4 +-
 ij/plugin/filter/MaximumFinder.java    |  57 ++++++++--
 ij/plugin/filter/ParticleAnalyzer.java |   8 +-
 ij/plugin/frame/ContrastAdjuster.java  |   6 +-
 ij/plugin/frame/Editor.java            | 117 ++++++++++++++++++--
 ij/plugin/frame/Recorder.java          |   2 +-
 ij/plugin/frame/RoiManager.java        |  50 ++++-----
 ij/plugin/frame/ThresholdAdjuster.java |  27 ++---
 ij/process/AutoThresholder.java        |   5 +-
 ij/process/ByteProcessor.java          |   1 -
 ij/process/ColorProcessor.java         | 104 +++++++++++++-----
 ij/process/ImageConverter.java         |   2 +
 ij/process/ImageProcessor.java         | 109 ++++++++++++-------
 ij/process/LUT.java                    |  13 ++-
 macros/CommandFinderTool.txt           |   3 +
 macros/StartupMacros.txt               |   5 +-
 release-notes.html                     |  58 +++-------
 88 files changed, 1633 insertions(+), 707 deletions(-)

diff --git a/IJ_Props.txt b/IJ_Props.txt
index aecadc9..c6ae54b 100644
--- a/IJ_Props.txt
+++ b/IJ_Props.txt
@@ -201,7 +201,7 @@ zoom04="View 100%[5]",ij.plugin.Zoom("100%")
 zoom05="To Selection",ij.plugin.Zoom("to")
 zoom06="Scale to Fit",ij.plugin.Zoom("scale")
 zoom07="Set... ",ij.plugin.Zoom("set")
-#zoom07="Maximize",ij.plugin.Zoom("max")
+zoom08="Maximize",ij.plugin.Zoom("max")
 
 # Plugins installed in the Image/Overlay submenu
 overlay01="Add Selection...[b]",ij.plugin.OverlayCommands("add")
@@ -394,12 +394,13 @@ utilities04="Monitor Events...",ij.plugin.EventListener
 utilities05="Monitor Memory...",ij.plugin.frame.MemoryMonitor
 utilities06=-
 utilities07="Capture Screen[G]",ij.plugin.ScreenGrabber
-utilities08="Capture Image",ij.plugin.ScreenGrabber("image")
-utilities09=-
-utilities10="ImageJ Properties",ij.plugin.JavaProperties
-utilities11="Threads",ij.plugin.ThreadLister
-utilities12="Benchmark",ij.plugin.filter.Benchmark
-utilities13="Reset...",ij.plugin.SimpleCommands("reset")
+utilities08="Capture Delayed...",ij.plugin.ScreenGrabber("delay")
+utilities09="Capture Image",ij.plugin.ScreenGrabber("image")
+utilities10=-
+utilities11="ImageJ Properties",ij.plugin.JavaProperties
+utilities12="Threads",ij.plugin.ThreadLister
+utilities13="Benchmark",ij.plugin.filter.Benchmark
+utilities14="Reset...",ij.plugin.SimpleCommands("reset")
 
 # Plugins installed in the Plugins/New submenu
 new_01="Macro",ij.plugin.NewPlugin("macro")
diff --git a/ij/CompositeImage.java b/ij/CompositeImage.java
index 985985d..f74ae17 100644
--- a/ij/CompositeImage.java
+++ b/ij/CompositeImage.java
@@ -210,7 +210,7 @@ public class CompositeImage extends ImagePlus {
 				setupLuts(nChannels);
 				LUT cm = lut[currentChannel];
 				if (mode==COLOR)
-					ip.setColorModel(cm);
+					ip.setLut(cm);
 				if (!(cm.min==0.0&&cm.max==0.0))
 					ip.setMinAndMax(cm.min, cm.max);
 				if (!IJ.isMacro()) ContrastAdjuster.update();
diff --git a/ij/Executer.java b/ij/Executer.java
index 4211e2c..4488e6d 100644
--- a/ij/Executer.java
+++ b/ij/Executer.java
@@ -194,7 +194,7 @@ public class Executer implements Runnable {
 				ed.evaluateScript(".bsh");
 			else if (isPython)
 				ed.evaluateScript(".py");
-			else
+			else if (!name.contains("_Tool"))
 				IJ.runMacro(text);
 		}
 		return true;
diff --git a/ij/IJ.java b/ij/IJ.java
index 23a6517..10397cc 100644
--- a/ij/IJ.java
+++ b/ij/IJ.java
@@ -562,7 +562,7 @@ public class IJ {
 		Undo.reset();
 		System.gc();
 		lastErrorMessage = "out of memory";
-		String tot = Runtime.getRuntime().totalMemory()/1048576L+"MB";
+		String tot = Runtime.getRuntime().maxMemory()/1048576L+"MB";
 		if (!memMessageDisplayed)
 			log(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
 		log("<Out of memory>");
@@ -1333,9 +1333,11 @@ public class IJ {
 				WindowManager.setWindow(win);
 			}
 			long start = System.currentTimeMillis();
-			// timeout after 2 seconds unless current thread is event dispatch thread
+			// timeout after 1 second unless current thread is event dispatch thread
 			String thread = Thread.currentThread().getName();
-			int timeout = thread!=null&&thread.indexOf("EventQueue")!=-1?0:2000;
+			int timeout = thread!=null&&thread.indexOf("EventQueue")!=-1?0:1000;
+			if (IJ.isMacOSX() && IJ.isJava18() && timeout>0)
+				timeout = 250;  //work around OS X/Java 8 window activation bug
 			while (true) {
 				wait(10);
 				imp = WindowManager.getCurrentImage();
@@ -1811,7 +1813,7 @@ public class IJ {
 			path = updateExtension(path, ".txt");
 			format = "Text Image...";
 		} else if (format.indexOf("text")!=-1 || format.indexOf("txt")!=-1) {
-			if (path!=null && !path.endsWith(".xls") && !path.endsWith(".csv"))
+			if (path!=null && !path.endsWith(".xls") && !path.endsWith(".csv") && !path.endsWith(".tsv"))
 				path = updateExtension(path, ".txt");
 			format = "Text...";
 		} else if (format.indexOf("zip")!=-1) {
diff --git a/ij/ImageJ.java b/ij/ImageJ.java
index f640a38..7fcf14f 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.51i";
+	public static final String VERSION = "1.51p";
 	public static final String BUILD = "";
 	public static Color backgroundColor = new Color(237,237,237);
 	/** SansSerif, 12-point, plain font. */
@@ -169,7 +169,7 @@ public class ImageJ extends Frame implements ActionListener,
 		statusLine.addKeyListener(this);
 		statusLine.addMouseListener(this);
 		statusBar.add("Center", statusLine);
-		progressBar = new ProgressBar(120, 20);
+		progressBar = new ProgressBar(ProgressBar.WIDTH, ProgressBar.HEIGHT);
 		progressBar.addKeyListener(this);
 		progressBar.addMouseListener(this);
 		statusBar.add("East", progressBar);
@@ -192,6 +192,22 @@ public class ImageJ extends Frame implements ActionListener,
 			setAlwaysOnTop(Prefs.alwaysOnTop);
 			pack();
 			setVisible(true);
+			Dimension size = getSize();
+			if (size!=null) {
+				if (IJ.debugMode) IJ.log("size: "+size);
+				if (IJ.isWindows() && size.height>108) {
+					// workaround for IJ window layout and FileDialog freeze problems with Windows 10 Creators Update
+					IJ.wait(10);
+					pack();
+					if (IJ.debugMode) IJ.log("pack()");
+					if (!Prefs.jFileChooserSettingChanged)
+						Prefs.useJFileChooser = true;
+				} else if (IJ.isMacOSX()) {
+					Rectangle maxBounds = GUI.getMaxWindowBounds();
+					if (loc.x+size.width>maxBounds.x+maxBounds.width)
+						setLocation(loc.x, loc.y);
+				}
+			}
 		}
 		if (err1!=null)
 			IJ.error(err1);
@@ -268,6 +284,7 @@ public class ImageJ extends Frame implements ActionListener,
 		Rectangle maxBounds = GUI.getMaxWindowBounds();
 		int ijX = Prefs.getInt(IJ_X,-99);
 		int ijY = Prefs.getInt(IJ_Y,-99);
+		//System.out.println("getPreferredLoc1: "+ijX+" "+ijY+" "+maxBounds);
 		if (ijX>=maxBounds.x && ijY>=maxBounds.y && ijX<(maxBounds.x+maxBounds.width-75))
 			return new Point(ijX, ijY);
 		Dimension tbsize = toolbar.getPreferredSize();
@@ -496,7 +513,7 @@ public class ImageJ extends Frame implements ActionListener,
 					else if (zoomKey && keyCode==KeyEvent.VK_UP && !ignoreArrowKeys(imp) && Toolbar.getToolId()<Toolbar.SPARE6)
 							cmd="In [+]";
 					else if (roi!=null) {
-						if ((flags & KeyEvent.ALT_MASK) != 0)
+						if ((flags & KeyEvent.ALT_MASK)!=0 || (flags & KeyEvent.CTRL_MASK)!=0)
 							roi.nudgeCorner(keyCode);
 						else
 							roi.nudge(keyCode);
@@ -652,10 +669,12 @@ public class ImageJ extends Frame implements ActionListener,
 	/** Called once when ImageJ quits. */
 	public void savePreferences(Properties prefs) {
 		Point loc = getLocation();
+		if (IJ.isLinux()) {
+			Rectangle bounds = GUI.getMaxWindowBounds();
+			loc.y = bounds.y;
+		}
 		prefs.put(IJ_X, Integer.toString(loc.x));
 		prefs.put(IJ_Y, Integer.toString(loc.y));
-		//prefs.put(IJ_WIDTH, Integer.toString(size.width));
-		//prefs.put(IJ_HEIGHT, Integer.toString(size.height));
 	}
 
 	public static void main(String args[]) {
diff --git a/ij/ImagePlus.java b/ij/ImagePlus.java
index 6f66790..7fad5bb 100644
--- a/ij/ImagePlus.java
+++ b/ij/ImagePlus.java
@@ -43,6 +43,9 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 	/** 32-bit RGB color */
 	public static final int COLOR_RGB = 4;
 	
+	/** Title of image used by Flatten command */
+	public static final String flattenTitle = "flatten~canvas";
+	
 	/** True if any changes have been made to this image. */
 	public boolean changes;
 	
@@ -96,8 +99,8 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 
     /** Constructs an uninitialized ImagePlus. */
     public ImagePlus() {
-    	ID = --currentID;
-		title="null";
+		title = (this instanceof CompositeImage)?"composite":"null";
+		setID();
     }
     
     /** Constructs an ImagePlus from an Image or BufferedImage. The first 
@@ -105,15 +108,15 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		Throws an IllegalStateException if an error occurs while loading the image. */
     public ImagePlus(String title, Image img) {
 		this.title = title;
-    	ID = --currentID;
 		if (img!=null)
 			setImage(img);
+		setID();
     }
     
     /** Constructs an ImagePlus from an ImageProcessor. */
     public ImagePlus(String title, ImageProcessor ip) {
  		setProcessor(title, ip);
-   		ID = --currentID;
+   		setID();
     }
     
 	/** Constructs an ImagePlus from a TIFF, BMP, DICOM, FITS,
@@ -140,16 +143,21 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
      		setRoi(imp.getRoi());
    			if (isURL)
    				this.url = pathOrURL;
-   			ID = --currentID;
+   			setID();
     	}
     }
 
 	/** Constructs an ImagePlus from a stack. */
     public ImagePlus(String title, ImageStack stack) {
     	setStack(title, stack);
-    	ID = --currentID;
+    	setID();
     }
     
+    private void setID() {
+    	ID = --currentID;
+    	//IJ.log("New "+this);
+	}
+	   
 	/** Locks the image so other threads can test to see if it
 		is in use. Returns true if the image was successfully locked.
 		Beeps, displays a message in the status bar, and returns
@@ -353,7 +361,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 	}
 		
 	/** ImageCanvas.paint() calls this method when the
-		ImageProcessor has generated new image. */
+		ImageProcessor has generated a new image. */
 	public void updateImage() {
 		if (ip!=null)
 			img = ip.createImage();
@@ -399,6 +407,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		if (isVisible())
 			return;
 		win = null;
+		//if (ip!=null) throw new IllegalArgumentException();
 		if ((IJ.isMacro() && ij==null) || Interpreter.isBatchMode()) {
 			if (isComposite()) ((CompositeImage)this).reset();
 			ImagePlus img = WindowManager.getCurrentImage();
@@ -1553,6 +1562,9 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 				overlay2 = ip2.getOverlay();
 				if (overlay2!=null)
 					setOverlay(overlay2);
+				Properties props = ((VirtualStack)stack).getProperties();
+				if (props!=null)
+					setProperty("FHT", props.get("FHT"));
 				pixels = ip2.getPixels();
 			} else
 				pixels = stack.getPixels(currentSlice);
@@ -1657,8 +1669,10 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 		deleteRoi();
 		switch (Toolbar.getToolId()) {
 			case Toolbar.RECTANGLE:
-				int cornerDiameter = Toolbar.getRoundRectArcSize();
-				roi = new Roi(sx, sy, this, cornerDiameter);
+				if (Toolbar.getRectToolType()==Toolbar.ROTATED_RECT_ROI)
+					roi = new RotatedRectRoi(sx, sy, this);
+				else
+					roi = new Roi(sx, sy, this, Toolbar.getRoundRectArcSize());
 				break;
 			case Toolbar.OVAL:
 				if (Toolbar.getOvalToolType()==Toolbar.ELLIPSE_ROI)
@@ -1728,9 +1742,11 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 				if (rm!=null)
 					rm.deselect(roi);
 			}
-			roi.notifyListeners(RoiListener.DELETED);
-			if (roi instanceof PointRoi)
-				((PointRoi)roi).resetCounters();
+			if (roi!=null) {
+				roi.notifyListeners(RoiListener.DELETED);
+				if (roi instanceof PointRoi)
+					((PointRoi)roi).resetCounters();
+			}
 			roi = null;
 			if (ip!=null)
 				ip.resetRoi();
@@ -2065,6 +2081,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 
 	/** Copies attributes (name, ID, calibration, path) of the specified image to this image. */
 	public void copyAttributes(ImagePlus imp) {
+		if (IJ.debugMode) IJ.log("copyAttributes: "+imp.getID()+"  "+this.getID()+" "+imp+"   "+this);
 		if (imp==null || imp.getWindow()!=null)
 			throw new IllegalArgumentException("Souce image is null or displayed");
 		ID = imp.getID();
@@ -2476,7 +2493,9 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 	/** Returns a "flattened" version of this image, in RGB format. */
 	public ImagePlus flatten() {
 		if (IJ.debugMode) IJ.log("flatten");
+		IJ.wait(50); // wait for screen to be refreshed
 		ImagePlus imp2 = createImagePlus();
+		imp2.setTitle(flattenTitle);
 		ImageCanvas ic2 = new ImageCanvas(imp2);
 		imp2.flatteningCanvas = ic2;
 		imp2.setRoi(getRoi());
@@ -2689,7 +2708,7 @@ public class ImagePlus implements ImageObserver, Measurements, Cloneable {
 	}
 
     public String toString() {
-    	return "img["+getTitle()+" ("+width+"x"+height+"x"+getNChannels()+"x"+getNSlices()+"x"+getNFrames()+")]";
+    	return "img[\""+getTitle()+"\" ("+getID()+"), "+getBitDepth()+"-bit, "+width+"x"+height+"x"+getNChannels()+"x"+getNSlices()+"x"+getNFrames()+"]";
     }
     
     public void setIJMenuBar(boolean b) {
diff --git a/ij/Menus.java b/ij/Menus.java
index cab9bd9..6b9a510 100644
--- a/ij/Menus.java
+++ b/ij/Menus.java
@@ -207,7 +207,7 @@ public class Menus {
 		getMenu("Analyze>Gels", true);
 		Menu toolsMenu = getMenu("Analyze>Tools", true);
 
-		// the plugins will be added later, with a separator
+		// the plugins will be added later, after a separator
 		addPluginsMenu();
 
 		Menu window = getMenu("Window");
@@ -279,11 +279,16 @@ public class Menus {
 		addExample(submenu, "Array Functions", "Array_Functions.ijm");
 		addExample(submenu, "Dual Progress Bars", "Dual_Progress_Bars.ijm");
 		addExample(submenu, "Grab Viridis Colormap", "Grab_Viridis_Colormap.ijm");
-		addExample(submenu, "Tool", "Circle_Tool.ijm");
+		addExample(submenu, "Custom Measurement", "Custom_Measurement.ijm");
+		submenu.addSeparator();
+		addExample(submenu, "Circle Tool", "Circle_Tool.ijm");
+		addExample(submenu, "Star Tool", "Star_Tool.ijm");
 		submenu.addActionListener(listener);
 		menu.add(submenu);
 		submenu = new Menu("JavaScript");
 		addExample(submenu, "Sphere", "Sphere.js");
+		addExample(submenu, "Plasma Cloud", "Plasma_Cloud.js");
+		addExample(submenu, "Cloud Debugger", "Cloud_Debugger.js");
 		addExample(submenu, "Example Plot", "Example_Plot.js");
 		addExample(submenu, "Semi-log Plot", "Semi-log_Plot.js");
 		addExample(submenu, "Arrow Plot", "Arrow_Plot.js");
@@ -294,6 +299,8 @@ public class Menus {
 		addExample(submenu, "Stack Overlay", "Stack_Overlay.js");
 		addExample(submenu, "Dual Progress Bars", "Dual_Progress_Bars.js");
 		addExample(submenu, "Gamma Adjuster", "Gamma_Adjuster.js");
+		addExample(submenu, "Custom Measurement", "Custom_Measurement.js");
+		addExample(submenu, "Terabyte VirtualStack", "Terabyte_VirtualStack.js");
 		submenu.addActionListener(listener);
 		menu.add(submenu);
 		submenu = new Menu("BeanShell");
@@ -313,11 +320,12 @@ public class Menus {
 		menu.add(submenu);
 		submenu = new Menu("Java");
 		addExample(submenu, "Sphere", "Sphere_.java");
+		addExample(submenu, "Plasma Cloud", "Plasma_Cloud.java");
+		addExample(submenu, "Gamma Adjuster", "Gamma_Adjuster.java");
 		addExample(submenu, "Plugin", "My_Plugin.java");
 		addExample(submenu, "Plugin Filter", "Filter_Plugin.java");
 		addExample(submenu, "Plugin Frame", "Plugin_Frame.java");
 		addExample(submenu, "Plugin Tool", "Prototype_Tool.java");
-		addExample(submenu, "Gamma Adjuster", "Gamma_Adjuster.java");
 		submenu.addActionListener(listener);
 		menu.add(submenu);
 		menu.addSeparator();
@@ -514,6 +522,7 @@ public class Menus {
 		Plugins not listed in IJ_Prefs are added to the end
 		of the Plugins menu. */
 	void installPlugins() {
+		int nPlugins0 = nPlugins;
 		String value, className;
 		char menuCode;
 		Menu menu;
@@ -566,6 +575,10 @@ public class Menus {
 					installUserPlugin(pluginList[i]);
 			}
 		}
+		if ((nPlugins-nPlugins0)<=1 && IJ.getDir("imagej")!=null && IJ.getDir("imagej").startsWith("/private")) {
+			pluginsMenu.addSeparator();
+			addPlugInItem(pluginsMenu, "Why are Plugins Missing?", "ij.plugin.SimpleCommands(\"missing\")", 0, false);
+		}
 		installJarPlugins();
 		installMacros();
 	}
diff --git a/ij/Prefs.java b/ij/Prefs.java
index fb4b787..87aa158 100644
--- a/ij/Prefs.java
+++ b/ij/Prefs.java
@@ -54,7 +54,8 @@ 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, SHOW_ALL_POINTS=1<<7;
+		REVERSE_NEXT_PREVIOUS_ORDER=1<<5, AUTO_RUN_EXAMPLES=1<<6, SHOW_ALL_POINTS=1<<7,
+		DO_NOT_SAVE_WINDOW_LOCS=1<<8, JFILE_CHOOSER_CHANGED=1<<9;
 	public static final String OPTIONS2 = "prefs.options2";
     
 	/** file.separator system property */
@@ -163,8 +164,14 @@ public class Prefs {
 	public static boolean alwaysOnTop;
 	/** Automatically spline fit line selections */
 	public static boolean splineFitLines;
+	/** Enable this option to workaround a bug with some Linux window
+		managers that causes windows to wander down the screen. */
+	public static boolean doNotSaveWindowLocations = true;
+	/** Use JFileChooser setting changed/ */
+	public static boolean jFileChooserSettingChanged;
+	/** Convert tiff units to microns if pixel width is less than 0.0001 cm. */
+	public static boolean convertToMicrons = true;
 
-	
 
 	static Properties ijPrefs = new Properties();
 	static Properties props = new Properties(ijPrefs);
@@ -477,6 +484,8 @@ public class Prefs {
 		reverseNextPreviousOrder = (options2&REVERSE_NEXT_PREVIOUS_ORDER)!=0;
 		autoRunExamples = (options2&AUTO_RUN_EXAMPLES)!=0;
 		showAllPoints = (options2&SHOW_ALL_POINTS)!=0;
+		doNotSaveWindowLocations = (options2&DO_NOT_SAVE_WINDOW_LOCS)!=0;
+		jFileChooserSettingChanged = (options2&JFILE_CHOOSER_CHANGED)!=0;
 	}
 
 	static void saveOptions(Properties prefs) {
@@ -502,7 +511,9 @@ 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) + (showAllPoints?SHOW_ALL_POINTS:0);
+			+ (autoRunExamples?AUTO_RUN_EXAMPLES:0) + (showAllPoints?SHOW_ALL_POINTS:0)
+			+ (doNotSaveWindowLocations?DO_NOT_SAVE_WINDOW_LOCS:0)
+			+ (jFileChooserSettingChanged?JFILE_CHOOSER_CHANGED:0);
 		prefs.put(OPTIONS2, Integer.toString(options2));
 	}
 
@@ -579,7 +590,8 @@ public class Prefs {
 	/** Saves the Point <code>loc</code> in the preferences
 		 file as a string using the keyword <code>key</code>. */
 	public static void saveLocation(String key, Point loc) {
-		set(key, loc.x+","+loc.y);
+		if (!doNotSaveWindowLocations)
+			set(key, loc.x+","+loc.y);
 	}
 
 	/** Uses the keyword <code>key</code> to retrieve a location
@@ -656,7 +668,7 @@ public class Prefs {
 	}
 	
 	public static String defaultResultsExtension() {
-		return get("options.ext", ".xls");
+		return get("options.ext", ".csv");
 	}
 		
 }
diff --git a/ij/VirtualStack.java b/ij/VirtualStack.java
index 012b1ed..9ad04ed 100644
--- a/ij/VirtualStack.java
+++ b/ij/VirtualStack.java
@@ -4,8 +4,9 @@ import ij.io.*;
 import ij.gui.ImageCanvas;
 import ij.util.Tools;
 import java.io.*;
-import java.awt.Font;
+import java.awt.*;
 import java.awt.image.ColorModel;
+import java.util.Properties;
 
 /** This class represents an array of disk-resident images. */
 public class VirtualStack extends ImageStack {
@@ -15,7 +16,8 @@ public class VirtualStack extends ImageStack {
 	private String[] names;
 	private String[] labels;
 	private int bitDepth;
-	private ImageProcessor ip;
+	private Properties  properties;
+
 	
 	/** Default constructor. */
 	public VirtualStack() { }
@@ -108,10 +110,9 @@ public class VirtualStack extends ImageStack {
 		were 1<=n<=nslices. Returns null if the stack is empty.
 	*/
 	public ImageProcessor getProcessor(int n) {
-		//IJ.log("getProcessor: "+n+"  "+names[n-1]+"  "+bitDepth);
 		if (path==null) {
-			if (ip==null)
-				ip = new ByteProcessor(getWidth(), getHeight());
+			ImageProcessor ip = new ByteProcessor(getWidth(), getHeight());
+			label(ip, ""+n, Color.white);
 			return ip;
 		}
 		Opener opener = new Opener();
@@ -132,18 +133,13 @@ public class VirtualStack extends ImageStack {
 			depthThisImage = imp.getBitDepth();
 			ip = imp.getProcessor();
 			ip.setOverlay(imp.getOverlay());
+			properties = imp.getProperty("FHT")!=null?imp.getProperties():null;
 		} else {
 			File f = new File(path, names[n-1]);
 			String msg = f.exists()?"Error opening ":"File not found: ";
 			ip = new ByteProcessor(getWidth(), getHeight());
 			ip.invert();
-			int size = getHeight()/20;
-			if (size<9) size=9;
-			Font font = new Font("Helvetica", Font.PLAIN, size);
-			ip.setFont(font);
-			ip.setAntialiasedText(true);
-			ip.setColor(0);
-			ip.drawString(msg+names[n-1], size, size*2);
+			label(ip, msg+names[n-1], Color.black);
 			depthThisImage = 8;
 		}
 		if (depthThisImage!=bitDepth) {
@@ -161,6 +157,16 @@ public class VirtualStack extends ImageStack {
 		}
 		return ip;
 	 }
+	 
+	 private void label(ImageProcessor ip, String msg, Color color) {
+		int size = getHeight()/20;
+		if (size<9) size=9;
+		Font font = new Font("Helvetica", Font.PLAIN, size);
+		ip.setFont(font);
+		ip.setAntialiasedText(true);
+		ip.setColor(color);
+		ip.drawString(msg, size, size*2);
+	}
  
 	/** Currently not implemented */
 	public int saveChanges(int n) {
@@ -236,6 +242,12 @@ public class VirtualStack extends ImageStack {
 		}
 		return this;
 	}
+	
+	/** Returns the ImagePlus Properties assoctated with the current slice, or null. */
+	public Properties getProperties() {
+		return properties;
+	}
+
 
 } 
 
diff --git a/ij/gui/EllipseRoi.java b/ij/gui/EllipseRoi.java
index ae40004..9ccb62c 100644
--- a/ij/gui/EllipseRoi.java
+++ b/ij/gui/EllipseRoi.java
@@ -6,7 +6,7 @@ import ij.plugin.frame.Recorder;
 import ij.process.FloatPolygon;
 import ij.measure.Calibration;
 
-/** Elliptical region of interest. */
+/** This class implements the ellipse selection tool. */
 public class EllipseRoi extends PolygonRoi {
 	private static final int vertices = 72;
 	private static double defaultRatio = 0.6;
@@ -21,6 +21,7 @@ public class EllipseRoi extends PolygonRoi {
 		this.aspectRatio = aspectRatio;
 		makeEllipse(x1, y1, x2, y2);
 		state = NORMAL;
+		bounds = null;
 	}
 
 	public EllipseRoi(int sx, int sy, ImagePlus imp) {
@@ -28,6 +29,8 @@ public class EllipseRoi extends PolygonRoi {
 		type = FREEROI;
 		xstart = ic.offScreenXD(sx);
 		ystart = ic.offScreenYD(sy);
+		setDrawOffset(false);
+		bounds = null;
 	}
 
 	public void draw(Graphics g) {
@@ -83,19 +86,14 @@ public class EllipseRoi extends PolygonRoi {
 		y = r.y;
 		width = r.width;
 		height = r.height;
-		bounds = poly.getFloatBounds();
-		float xbase = (float)bounds.getX();
-		float ybase = (float)bounds.getY();
 		for (int i=0; i<nPoints; i++) {
-			xpf[i] = xpf[i]-xbase;
-			ypf[i] = ypf[i]-ybase;
+			xpf[i] = xpf[i]-x;
+			ypf[i] = ypf[i]-y;
 		}
 	}
 	
 	protected void handleMouseUp(int screenX, int screenY) {
 		if (state==CONSTRUCTING) {
-            addOffset();
-			finishPolygon();
 			if (Recorder.record) {
 				double x1 = xpf[handle[2]]+x;
 				double y1 = ypf[handle[2]]+y;
@@ -113,23 +111,18 @@ public class EllipseRoi extends PolygonRoi {
 	protected void moveHandle(int sx, int sy) {
 		double ox = ic.offScreenXD(sx); 
 		double oy = ic.offScreenYD(sy);
-		double xbase=x, ybase=y;
-		if (bounds!=null) {
-			xbase = bounds.x;
-			ybase = bounds.y;
-		}
-		double x1 = xpf[handle[2]]+xbase;
-		double y1 = ypf[handle[2]]+ybase;
-		double x2 = xpf[handle[0]]+xbase;
-		double y2 = ypf[handle[0]]+ybase;
+		double x1 = xpf[handle[2]]+x;
+		double y1 = ypf[handle[2]]+y;
+		double x2 = xpf[handle[0]]+x;
+		double y2 = ypf[handle[0]]+y;
 		switch(activeHandle) {
 			case 0: 
 				x2 = ox;
 				y2 = oy;
 				break;
 			case 1: 
-				double dx = (xpf[handle[3]]+xbase) - ox;
-				double dy = (ypf[handle[3]]+ybase) - oy;
+				double dx = (xpf[handle[3]]+x) - ox;
+				double dy = (ypf[handle[3]]+y) - oy;
 				updateRatio(Math.sqrt(dx*dx+dy*dy), x1, y1, x2, y2);
 				break;
 			case 2: 
@@ -137,8 +130,8 @@ public class EllipseRoi extends PolygonRoi {
 				y1 = oy;
 				break;
 			case 3: 
-				dx = (xpf[handle[1]]+xbase) - ox;
-				dy = (ypf[handle[1]]+ybase) - oy;
+				dx = (xpf[handle[1]]+x) - ox;
+				dy = (ypf[handle[1]]+y) - oy;
 				updateRatio(Math.sqrt(dx*dx+dy*dy), x1, y1, x2, y2);
 				break;
 		}
@@ -192,16 +185,11 @@ public class EllipseRoi extends PolygonRoi {
 
 	/** Returns x1, y1, x2, y2 and aspectRatio as a 5 element array. */
 	public double[] getParams() {
-		double xbase=x, ybase=y;
-		if (bounds!=null) {
-			xbase = bounds.x;
-			ybase = bounds.y;
-		}
 		double[] params = new double[5];
-		params[0] = xpf[handle[2]]+xbase;
-		params[1]  = ypf[handle[2]]+ybase;
-		params[2]  = xpf[handle[0]]+xbase;
-		params[3]  = ypf[handle[0]]+ybase;
+		params[0] = xpf[handle[2]]+x;
+		params[1]  = ypf[handle[2]]+y;
+		params[2]  = xpf[handle[0]]+x;
+		params[3]  = ypf[handle[0]]+y;
 		params[4]  = aspectRatio;
 		return params;
 	}
diff --git a/ij/gui/GUI.java b/ij/gui/GUI.java
index 04e7825..028f28b 100644
--- a/ij/gui/GUI.java
+++ b/ij/gui/GUI.java
@@ -135,9 +135,6 @@ public class GUI {
     
     /** Lightens overly dark scrollbar background on Windows 8. */
     public static void fix(Scrollbar sb) {
-    	if (isWindows8) {
-			sb.setBackground(lightGray);
-		}
     }
     
     public static boolean showCompositeAdvisory(ImagePlus imp, String title) {
diff --git a/ij/gui/GenericDialog.java b/ij/gui/GenericDialog.java
index e251c9e..4c5a7fb 100644
--- a/ij/gui/GenericDialog.java
+++ b/ij/gui/GenericDialog.java
@@ -40,7 +40,6 @@ import ij.macro.*;
 public class GenericDialog extends Dialog implements ActionListener, TextListener, 
 FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 
-	public static final int MAX_SLIDERS = 25;
 	protected Vector numberField, stringField, checkbox, choice, slider, radioButtonGroups;
 	protected TextArea textArea1, textArea2;
 	protected Vector defaultValues,defaultText,defaultStrings,defaultChoiceIndexes;
@@ -58,14 +57,13 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 	private boolean firstSlider=true;
 	private boolean invalidNumber;
 	private String errorMessage;
-	private boolean firstPaint = true;
 	private Hashtable labels;
 	private boolean macro;
 	private String macroOptions;
 	private int topInset, leftInset, bottomInset;
     private boolean customInsets;
-    private int[] sliderIndexes;
-    private double[] sliderScales;
+    private Vector sliderIndexes;
+    private Vector sliderScales;
     private Checkbox previewCheckbox;    // the "Preview" Checkbox, if any
     private Vector dialogListeners;             // the Objects to notify on user input
     private PlugInFilterRunner pfr;      // the PlugInFilterRunner for automatic preview
@@ -81,6 +79,7 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
     private boolean smartRecording;
     private Vector imagePanels;
     private static GenericDialog instance;
+    private boolean firstPaint = true;
 
     /** Creates a new GenericDialog with the specified title. Uses the current image
     	image window as the parent frame or the ImageJ frame if no image windows
@@ -110,8 +109,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 			setForeground(SystemColor.controlText);
 			setBackground(SystemColor.control);
 		}
-		//if (IJ.isLinux())
-		//	setBackground(new Color(238, 238, 238));
 		grid = new GridBagLayout();
 		c = new GridBagConstraints();
 		setLayout(grid);
@@ -121,14 +118,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		addWindowListener(this);
     }
     
-	//void showFields(String id) {
-	//	String s = id+": ";
-	//	for (int i=0; i<maxItems; i++)
-	//		if (numberField[i]!=null)
-	//			s += i+"='"+numberField[i].getText()+"' ";
-	//	IJ.write(s);
-	//}
-
 	/** Adds a numeric field. The first word of the label must be
 		unique or command recording will not work.
 	* @param label			the label
@@ -309,7 +298,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		cb.addKeyListener(this);
 		add(cb);
 		checkbox.addElement(cb);
-		//ij.IJ.write("addCheckbox: "+ y+" "+cbIndex);
         if (!isPreview &&(Recorder.record || macro)) //preview checkbox is not recordable
 			saveLabel(cb, label);
         if (isPreview) previewCheckbox = cb;
@@ -352,9 +340,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
     public void addPreviewCheckbox(PlugInFilterRunner pfr, String label) {
         if (previewCheckbox!=null)
         	return;
-    	//ImagePlus imp = WindowManager.getCurrentImage();
-		//if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==IJ.COMPOSITE)
-		//	return;
         previewLabel = label;
         this.pfr = pfr;
         addCheckbox(previewLabel, false, true);
@@ -621,11 +606,10 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		
 		if (slider==null) {
 			slider = new Vector(5);
-			sliderIndexes = new int[MAX_SLIDERS];
-			sliderScales = new double[MAX_SLIDERS];
+			sliderIndexes = new Vector(5);
+			sliderScales = new Vector(5);
 		}
 		Scrollbar s = new Scrollbar(Scrollbar.HORIZONTAL, (int)defaultValue, 1, (int)minValue, (int)maxValue+1);
-		GUI.fix(s);
 		slider.addElement(s);
 		s.addAdjustmentListener(this);
 		s.setUnitIncrement(1);
@@ -644,8 +628,8 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		tf.addFocusListener(this);
 		tf.addKeyListener(this);
 		numberField.addElement(tf);
-		sliderIndexes[slider.size()-1] = numberField.size()-1;
-		sliderScales[slider.size()-1] = scale;
+		sliderIndexes.add(new Integer(numberField.size()-1));
+		sliderScales.add(new Double(scale));
 		defaultValues.addElement(new Double(defaultValue/scale));
 		defaultText.addElement(tf.getText());
 		tf.setEditable(true);
@@ -826,7 +810,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		if (macro) {
 			label = (String)labels.get((Object)tf);
 			theText = Macro.getValue(macroOptions, label, theText);
-			//IJ.write("getNextNumber: "+label+"  "+theText);
 		}	
 		String originalText = (String)defaultText.elementAt(nfIndex);
 		double defaultValue = ((Double)(defaultValues.elementAt(nfIndex))).doubleValue();
@@ -1167,8 +1150,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 			c.insets = new Insets(15, 0, 0, 0);
 			grid.setConstraints(buttons, c);
 			add(buttons);
-			if (IJ.isMacintosh())
-				setResizable(false);
 			if (IJ.isMacOSX()&&IJ.isJava18())
 				instance = this;
 			pack();
@@ -1176,8 +1157,9 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 			if (centerDialog) GUI.center(this);
 			setVisible(true);
 			recorderOn = Recorder.record;
-			IJ.wait(50); // work around for Sun/WinNT bug
+			IJ.wait(25);
 		}
+		
 		/* For plugins that read their input only via dialogItemChanged, call it at least once */
 		if (!wasCanceled && dialogListeners!=null && dialogListeners.size()>0) {
 			resetCounters();
@@ -1319,15 +1301,15 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		if (slider==null) return;
 		Object source = e.getSource();
 		for (int i=0; i<slider.size(); i++) {
-			int index = sliderIndexes[i];
+			int index = ((Integer)sliderIndexes.get(i)).intValue();
 			if (source==numberField.elementAt(index)) {
 				TextField tf = (TextField)numberField.elementAt(index);
 				double value = Tools.parseDouble(tf.getText());
 				if (!Double.isNaN(value)) {
 					Scrollbar sb = (Scrollbar)slider.elementAt(i);
-					sb.setValue((int)(value*sliderScales[i]));
+					double scale = ((Double)sliderScales.get(i)).doubleValue();
+					sb.setValue((int)(value*scale));
 				}	
-				//IJ.log(i+" "+tf.getText());
 			}
 		}
 	}
@@ -1338,6 +1320,7 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 
 	public void focusGained(FocusEvent e) {
 		Component c = e.getComponent();
+		//IJ.log("focusGained: "+c);
 		if (c instanceof TextField)
 			((TextField)c).selectAll();
 	}
@@ -1366,7 +1349,6 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		} 
 	} 
 		
-
 	void accessTextFields() {
 		if (stringField!=null) {
 			for (int i=0; i<stringField.size(); i++)
@@ -1401,9 +1383,11 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 		for (int i=0; i<slider.size(); i++) {
 			if (source==slider.elementAt(i)) {
 				Scrollbar sb = (Scrollbar)source;
-				TextField tf = (TextField)numberField.elementAt(sliderIndexes[i]);
-				int digits = sliderScales[i]==1.0?0:2;
-				tf.setText(""+IJ.d2s(sb.getValue()/sliderScales[i],digits));
+				int index = ((Integer)sliderIndexes.get(i)).intValue();
+				TextField tf = (TextField)numberField.elementAt(index);
+				double scale = ((Double)sliderScales.get(i)).doubleValue();
+				int digits = scale==1.0?0:2;
+				tf.setText(""+IJ.d2s(sb.getValue()/scale,digits));
 			}
 		}
 	}
@@ -1446,22 +1430,18 @@ FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
 				((ImagePanel)imagePanels.get(i)).repaint();
 		}
 	}
-
+	
 	public void paint(Graphics g) {
 		super.paint(g);
-		if (firstPaint) {
-			if (numberField!=null && IJ.isMacOSX()) {
-				// work around for bug on Intel Macs that caused 1st field to be un-editable
-				TextField tf = (TextField)(numberField.elementAt(0));
-				tf.setEditable(false);
-				tf.setEditable(true);
-			}
-			if (numberField==null && stringField==null)
-				okay.requestFocus();
+		if (firstPaint && IJ.isMacOSX() && IJ.isJava18()) {
+			IJ.wait(25);
+			Dimension size = getSize();
+			if (size!=null)
+				setSize(size.width+2,size.height+2);
 			firstPaint = false;
 		}
 	}
-    	
+    
     public void windowClosing(WindowEvent e) {
 		wasCanceled = true; 
 		dispose(); 
diff --git a/ij/gui/HistogramWindow.java b/ij/gui/HistogramWindow.java
index a9be888..8cd7fb2 100644
--- a/ij/gui/HistogramWindow.java
+++ b/ij/gui/HistogramWindow.java
@@ -23,7 +23,7 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 	static final int BAR_HEIGHT = 12;
 	static final int XMARGIN = 20;
 	static final int YMARGIN = 10;
-	static final int INTENSITY=0, RED=1, GREEN=2, BLUE=3;
+	static final int INTENSITY1=0, INTENSITY2=1, RGB=2, RED=3, GREEN=4, BLUE=5;
 	
 	protected ImageStatistics stats;
 	protected long[] histogram;
@@ -45,7 +45,7 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 	private ImagePlus srcImp;		// source image for live histograms
 	private Thread bgThread;		// thread background drawing
 	private boolean doUpdate;	// tells background thread to update
-	private int channel;				// RGB channel
+	private int rgbMode = -1;
 	private String blankLabel;
 	private boolean stackHistogram;
 	    
@@ -96,19 +96,41 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 		the same as the image range expect for 32 bit images. */
 	public void showHistogram(ImagePlus imp, int bins, double histMin, double histMax) {
 		boolean limitToThreshold = (Analyzer.getMeasurements()&LIMIT)!=0;
-		if (channel!=INTENSITY && imp.getType()==ImagePlus.COLOR_RGB) {
+		if (imp.getBitDepth()==24 && rgbMode<INTENSITY1)
+			rgbMode=INTENSITY1;
+		if (rgbMode==RED||rgbMode==GREEN||rgbMode==BLUE) {
+			int channel = rgbMode - 2;
 			ColorProcessor cp = (ColorProcessor)imp.getProcessor();
 			ImageProcessor ip = cp.getChannel(channel, null);
 			ImagePlus imp2 = new ImagePlus("", ip);
 			imp2.setRoi(imp.getRoi());
 			stats = imp2.getStatistics(AREA+MEAN+MODE+MIN_MAX, bins, histMin, histMax);
-		} else
+		} else if (rgbMode==RGB)
+			stats = RGBHistogram(imp, bins, histMin, histMax);
+		else
 			stats = imp.getStatistics(AREA+MEAN+MODE+MIN_MAX+(limitToThreshold?LIMIT:0), bins, histMin, histMax);
 		showHistogram(imp, stats);
 	}
+	
+	private ImageStatistics RGBHistogram(ImagePlus imp, int bins, double histMin, double histMax) {
+		ImageProcessor ip = (ColorProcessor)imp.getProcessor();
+		ip = ip.crop();
+		int w = ip.getWidth();
+		int h = ip.getHeight();
+		ImageProcessor ip2 = new ByteProcessor(w*3, h);
+		ByteProcessor temp = null;
+		for (int i=0; i<3; i++) {
+			temp = ((ColorProcessor)ip).getChannel(i+1,temp);
+			ip2.insert(temp, i*w, 0);
+		}
+		ImagePlus imp2 = new ImagePlus("imp2", ip2);
+		return imp2.getStatistics(AREA+MEAN+MODE+MIN_MAX, bins, histMin, histMax);
+	}
 
 	/** Draws the histogram using the specified title and ImageStatistics. */
 	public void showHistogram(ImagePlus imp, ImageStatistics stats) {
+		if (imp.getBitDepth()==24 && rgbMode<INTENSITY1)
+			rgbMode=INTENSITY1;
 		stackHistogram = stats.stackStatistics;
 		if (list==null)
 			setup(imp);
@@ -180,6 +202,10 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 		}
 		add(buttons);
 		pack();
+		if (IJ.isMacOSX() && IJ.isJava18()) {
+			IJ.wait(50);
+			pack();
+		}
     }
     
 	public void setup() {setup(null);}
@@ -215,8 +241,7 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 		int x, y;
 		long maxCount2 = 0;
 		int mode2 = 0;
-		long saveModalCount;
-		    	
+		long saveModalCount;		    	
 		ip.setColor(Color.black);
 		ip.setLineWidth(1);
 		decimalPlaces = Analyzer.getPrecision();
@@ -229,10 +254,8 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
   			}
   		}
 		newMaxCount = histogram[stats.mode];
-		if ((newMaxCount>(maxCount2 * 2)) && (maxCount2 != 0)) {
+		if ((newMaxCount>(maxCount2 * 2)) && (maxCount2 != 0))
 			newMaxCount = (int)(maxCount2 * 1.5);
-  			//histogram[stats.mode] = newMaxCount;
-		}
 		if (logScale || IJ.shiftKeyDown() && !liveMode())
 			drawLogPlot(yMax>0?yMax:newMaxCount, ip);
 		drawPlot(yMax>0?yMax:newMaxCount, ip);
@@ -252,13 +275,13 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 		ImageProcessor ipSource = imp.getProcessor();
 		float[] pixels = null;
 		ImageProcessor ipRamp = null;
-		if (ipSource instanceof ColorProcessor) {
+		if (rgbMode>=INTENSITY1) {
 			ipRamp = new FloatProcessor(width, height);
-			if (channel==RED)
+			if (rgbMode==RED)
 				ipRamp.setColorModel(LUT.createLutFromColor(Color.red));
-			else if (channel==GREEN)
+			else if (rgbMode==GREEN)
 				ipRamp.setColorModel(LUT.createLutFromColor(Color.green));
-			else if (channel==BLUE)
+			else if (rgbMode==BLUE)
 				ipRamp.setColorModel(LUT.createLutFromColor(Color.blue));
 			pixels = (float[])ipRamp.getPixels();
 		} else
@@ -370,7 +393,21 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 			range = 256;
 		ip.drawString(d2s(hmin), x - 4, y);
 		ip.drawString(d2s(hmax), x + HIST_WIDTH - getWidth(hmax, ip) + 10, y);
-        
+		if (rgbMode>=INTENSITY1) {
+			x += HIST_WIDTH/2;
+			y += 1;
+			ip.setJustification(ImageProcessor.CENTER_JUSTIFY);
+			boolean weighted = ((ColorProcessor)ip).weightedHistogram();
+			switch (rgbMode) {
+				case INTENSITY1: ip.drawString((weighted?"Intensity (weighted)":"Intensity (unweighted)"), x, y); break;
+				case INTENSITY2: ip.drawString((weighted?"Intensity (unweighted)":"Intensity (weighted)"), x, y); break;
+				case RGB: ip.drawString("R+G+B", x, y); break;
+				case RED: ip.drawString("Red", x, y); break;
+				case GREEN: ip.drawString("Green", x, y); break;
+				case BLUE: ip.drawString("Blue", x, y);  break;
+			}
+			ip.setJustification(ImageProcessor.LEFT_JUSTIFY);
+		}        
 		double binWidth = range/stats.nBins;
 		binWidth = Math.abs(binWidth);
 		boolean showBins = binWidth!=1.0 || !fixedRange;
@@ -396,21 +433,6 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 			ip.drawString("Bin Width: " + d2s(binWidth), col2, row4);
 		}
 	}
-
-	/*
-	String d2s(double d) {
-		if (d==Double.MAX_VALUE||d==-Double.MAX_VALUE)
-			return "0";
-		else if (Double.isNaN(d))
-			return("NaN");
-		else if (Double.isInfinite(d))
-			return("Infinity");
-		else if ((int)d==d)
-			return ResultsTable.d2s(d,0);
-		else
-			return ResultsTable.d2s(d,decimalPlaces);
-	}
-	*/
 	
 	private String d2s(double d) {
 		if ((int)d==d)
@@ -542,23 +564,23 @@ public class HistogramWindow extends ImageWindow implements Measurements, Action
 	
 	private void changeChannel() {
 		ImagePlus imp = WindowManager.getImage(srcImageID);
-		if (imp==null || imp.getType()!=ImagePlus.COLOR_RGB) {
-			channel = INTENSITY;
+		if (imp==null || imp.getType()!=ImagePlus.COLOR_RGB)
 			return;
-		} else {
-			channel++;
-			if (channel>BLUE) channel=INTENSITY;
-			showHistogram(imp, 256);
-			String name = this.imp.getTitle();
-			if (name.startsWith("Red ")) name=name.substring(4);
-			else if (name.startsWith("Green ")) name=name.substring(6);
-			else if (name.startsWith("Blue ")) name=name.substring(5);
-			switch (channel) {
-				case INTENSITY: this.imp.setTitle(name); break;
-				case RED: this.imp.setTitle("Red "+name); break;
-				case GREEN: this.imp.setTitle("Green "+name); break;
-				case BLUE: this.imp.setTitle("Blue "+name); break;
-			}
+		else {
+			rgbMode++;
+			if (rgbMode>BLUE) rgbMode=INTENSITY1;
+			ColorProcessor cp = (ColorProcessor)imp.getProcessor();
+			boolean weighted = cp.weightedHistogram();
+			if (rgbMode==INTENSITY2) {
+				double[] weights = cp.getRGBWeights();
+				if (weighted)
+					cp.setRGBWeights(1d/3d, 1d/3d, 1d/3d);
+				else
+					cp.setRGBWeights(0.299, 0.587, 0.114);
+				showHistogram(imp, 256);
+				cp.setRGBWeights(weights);
+			} else
+				showHistogram(imp, 256);
 		}
 	}
 
diff --git a/ij/gui/ImageCanvas.java b/ij/gui/ImageCanvas.java
index 0b3c57a..2aafa88 100644
--- a/ij/gui/ImageCanvas.java
+++ b/ij/gui/ImageCanvas.java
@@ -83,6 +83,7 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 	private boolean scaleToFit;
 	private boolean painted;
 	private boolean hideZoomIndicator;
+	private boolean flattening;
 		
 	public ImageCanvas(ImagePlus imp) {
 		this.imp = imp;
@@ -98,7 +99,8 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
  		addMouseListener(this);
  		addMouseMotionListener(this);
  		addKeyListener(ij);  // ImageJ handles keyboard shortcuts
-		setFocusTraversalKeysEnabled(false);
+ 		setFocusTraversalKeysEnabled(false);
+		//setScaleToFit(true);
 	}
 		
 	void updateImage(ImagePlus imp) {
@@ -208,7 +210,7 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 		if (roi!=null || overlay!=null || showAllOverlay!=null || Prefs.paintDoubleBuffered) {
 			if (roi!=null)
 				roi.updatePaste();
-			if (!IJ.isMacOSX() && imageWidth!=0) {
+			if (imageWidth!=0) {
 				paintDoubleBuffered(g);
 				setPaintPending(false);
 				return;
@@ -284,6 +286,7 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 	private void drawOverlay(Overlay overlay, Graphics g) {
 		if (imp!=null && imp.getHideOverlay() && overlay!=showAllOverlay)
 			return;
+		flattening = imp!=null && ImagePlus.flattenTitle.equals(imp.getTitle());
 		if (imp!=null && showAllOverlay!=null && overlay!=showAllOverlay)
 			overlay.drawLabels(false);
 		Color labelColor = overlay.getLabelColor();
@@ -458,8 +461,8 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 				labelRects[index] = new Rectangle(x2-crossSize2, y2-crossSize2, crossSize, crossSize);
 			} else
 				labelRects[index] = new Rectangle(x-3, y-h+1, w+4, h);
-		}
-		//if (IJ.debugMode && index==0) IJ.log("drawRoiLabel: "+drawingList+" "+label+" "+x+" "+y);
+		}		
+		//IJ.log("drawRoiLabel: "+" "+label+" "+x+" "+y+" "+flattening);
 		g.setColor(labelColor);
 		g.drawString(label, x+xoffset, y-2+yoffset);
 		g.setColor(defaultColor);
@@ -790,6 +793,7 @@ public class ImageCanvas extends Canvas implements MouseListener, MouseMotionLis
 		Note that sx and sy are screen coordinates. */
 	public void zoomIn(int sx, int sy) {
 		if (magnification>=32) return;
+		scaleToFit = false;
 	    boolean mouseMoved = sqr(sx-lastZoomSX) + sqr(sy-lastZoomSY) > MAX_MOUSEMOVE_ZOOM*MAX_MOUSEMOVE_ZOOM;
 		lastZoomSX = sx;
 		lastZoomSY = sy;
diff --git a/ij/gui/ImageWindow.java b/ij/gui/ImageWindow.java
index 8466c53..596f357 100644
--- a/ij/gui/ImageWindow.java
+++ b/ij/gui/ImageWindow.java
@@ -185,7 +185,6 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 		int screenHeight = maxWindow.y+maxWindow.height-sliderHeight;
 		double mag = 1;
 		while (xbase+width*mag>maxWindow.x+maxWindow.width || ybase+height*mag>=screenHeight) {
-			//IJ.log(mag+"  "+xbase+"  "+width*mag+"  "+maxWindow.width);
 			double mag2 = ImageCanvas.getLowerZoomLevel(mag);
 			if (mag2==mag) break;
 			mag = mag2;
@@ -198,14 +197,15 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 		ic.setMagnification(mag);
 		if (y+height*mag>screenHeight)
 			y = ybase;
-        if (!updating) setLocation(x, y);
-		if (Prefs.open100Percent && ic.getMagnification()<1.0) {
+        if (Prefs.open100Percent && ic.getMagnification()<1.0) {
 			while(ic.getMagnification()<1.0)
 				ic.zoomIn(0, 0);
 			setSize(Math.min(width, maxWindow.width-x), Math.min(height, screenHeight-y));
 			validate();
 		} else 
 			pack();
+		if (!updating)
+			setLocation(x, y);
 	}
 					
 	Rectangle getMaxWindow(int xloc, int yloc) {
@@ -380,7 +380,6 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
     }
 
     public void paint(Graphics g) {
-		//if (IJ.debugMode) IJ.log("wPaint: " + imp.getTitle());
 		drawInfo(g);
 		Rectangle r = ic.getBounds();
 		int extraWidth = MIN_WIDTH - r.width;
@@ -513,7 +512,6 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 		int extraHeight = insets.top+insets.bottom + 10;
 		if (extraHeight==20) extraHeight = 42;
 		int members = getComponentCount();
-		//if (IJ.debugMode) IJ.log("getExtraSize: "+members+" "+insets);
 		for (int i=1; i<members; i++) {
 		    Component m = getComponent(i);
 		    Dimension d = m.getPreferredSize();
@@ -576,14 +574,12 @@ public class ImageWindow extends Frame implements FocusListener, WindowListener,
 	}
 	
 	public void windowClosing(WindowEvent e) {
-		//IJ.log("windowClosing: "+imp.getTitle()+" "+closed);
 		if (closed)
 			return;
 		if (ij!=null) {
 			WindowManager.setCurrentWindow(this);
 			IJ.doCommand("Close");
 		} else {
-			//setVisible(false);
 			dispose();
 			WindowManager.removeWindow(this);
 		}
diff --git a/ij/gui/Line.java b/ij/gui/Line.java
index 83db8de..7ef6888 100644
--- a/ij/gui/Line.java
+++ b/ij/gui/Line.java
@@ -6,6 +6,8 @@ import ij.plugin.Straightener;
 import ij.plugin.frame.Recorder;
 import java.awt.*;
 import java.awt.image.*;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
 import java.awt.event.*;
 import java.awt.geom.*;
 
@@ -625,5 +627,92 @@ public class Line extends Roi {
 		x1d=xx+x1R; y1d=yy+y1R; x2d=xx+x2R; y2d=yy+y2R;
 		x1=(int)x1d; y1=(int)y1d; x2=(int)x2d; y2=(int)y2d;
 	}
+	
+	public FloatPolygon getRotationCenter() {
+		double xcenter = x1d + (x2d-x1d)/2.0;
+		double ycenter = y1d + (y2d-y1d)/2.0;
+		FloatPolygon p = new FloatPolygon();
+		p.addPoint(xcenter,ycenter);
+		return p;
+	}
+	
+	/**
+	 * Dedicated point iterator for thin lines.
+	 * The iterator is based on (an improved version of) the algorithm used by
+	 * the original method {@code ImageProcessor.getLine(double, double, double, double)}.
+	 * Improvements are (a) that the endpoint is drawn too and (b) every line
+	 * point is visited only once, duplicates are skipped.
+	 * 
+	 * Author: Wilhelm Burger (04/2017)
+	*/
+	public static class PointIterator implements Iterator<Point> {
+		private double x1, y1;
+		private final int n;
+		private final double xinc, yinc;
+		private double x, y;
+		private int u, v;
+		private int u_prev, v_prev;
+		private int i;
+
+		public PointIterator(Line line) {
+			this(line.x1d, line.y1d, line.x2d, line.y2d);
+		}
+		
+		public PointIterator(double x1, double y1, double x2, double y2) {
+			this.x1 = x1;
+			this.y1 = y1;
+			double dx = x2 - x1;
+			double dy = y2 - y1;
+			this.n = (int) Math.ceil(Math.sqrt(dx * dx + dy * dy));
+			this.xinc = dx / n;
+			this.yinc = dy / n;
+			x = x1;
+			y = y1;
+			u = (int) Math.round(x - 0.5);	
+			v = (int) Math.round(y - 0.5);
+			u_prev = Integer.MIN_VALUE;
+			v_prev = Integer.MIN_VALUE;
+			i = 0;
+		}
+
+		@Override
+		public boolean hasNext() {
+			return i <= n;	// needs to be '<=' to include last segment (point)!
+		}
+
+		@Override
+		public Point next() {
+			if (i > n) throw new NoSuchElementException();
+			Point p = new Point(u, v);	// the current (next) point
+			moveToNext();
+			return p;
+		}
+		
+		// move to next point by skipping duplicate points
+		private void moveToNext() {
+			do {
+				i = i + 1;
+				x = x1 + i * xinc;
+				y = y1 + i * yinc; 
+				u_prev = u;
+				v_prev = v;
+				u = (int) Math.round(x - 0.5);	
+				v = (int) Math.round(y - 0.5);
+			} while (i <= n && u == u_prev && v == v_prev);
+		}
+		
+		@Override
+		public void remove() {
+			throw new UnsupportedOperationException();
+		}
+	}
+	
+	@Override
+	public Iterator<Point> iterator() {
+		if (getStrokeWidth() <= 1.0)
+			return new PointIterator(this);	// use the specific thin-line iterator
+		else
+			return super.iterator();	// fall back on Roi's iterator
+	}
 
 }
diff --git a/ij/gui/NewImage.java b/ij/gui/NewImage.java
index 9d1f037..b24524d 100644
--- a/ij/gui/NewImage.java
+++ b/ij/gui/NewImage.java
@@ -94,7 +94,7 @@ public class NewImage {
 						break;
 					case RGB: pixels2 = new int[width*height];
 						if (fill==FILL_RANDOM)
-							fillRandomRGB(new ColorProcessor(width,height,(int[])pixels2));
+							fillRandomRGB(new ColorProcessor(width,height,(int[])pixels2), false);
 						break;
 				}
 				if ((fill==FILL_WHITE||fill==FILL_RAMP) || ((type==RGB)&&(fill!=FILL_RANDOM)))
@@ -194,7 +194,7 @@ public class NewImage {
 				}
 				break;
 			case FILL_RANDOM:
-				fillRandomRGB(ip);
+				fillRandomRGB(ip, true);
 				break;
 		}
 		ImagePlus imp = new ImagePlus(title, ip);
@@ -205,12 +205,18 @@ public class NewImage {
 		return imp;
 	}
 	
-	private static void fillRandomRGB(ColorProcessor ip) {
+	private static void fillRandomRGB(ColorProcessor ip, boolean sp) {
 		ByteProcessor rr = new ByteProcessor(width, height);
 		ByteProcessor gg = new ByteProcessor(width, height);
 		ByteProcessor bb = new ByteProcessor(width, height);
-		rr.add(127); gg.add(127); bb.add(127);
-		rr.noise(31); gg.noise(31); bb.noise(31);
+		if (sp) IJ.showProgress(0.0);
+		rr.add(127); if (sp) IJ.showProgress(0.05);
+		gg.add(127); if (sp) IJ.showProgress(0.10);
+		bb.add(127); if (sp) IJ.showProgress(0.15);
+		rr.noise(31); if (sp) IJ.showProgress(0.40);
+		gg.noise(31); if (sp) IJ.showProgress(0.65);
+		bb.noise(31); if (sp) IJ.showProgress(0.90);
+		if (sp) IJ.showProgress(1.0);
 		ip.setChannel(1,rr); ip.setChannel(2,gg); ip.setChannel(3,bb);
 	}
 
diff --git a/ij/gui/Overlay.java b/ij/gui/Overlay.java
index 0ec378b..49f9067 100644
--- a/ij/gui/Overlay.java
+++ b/ij/gui/Overlay.java
@@ -4,6 +4,8 @@ import java.util.Vector;
 import java.awt.geom.Rectangle2D;
 import ij.*;
 import ij.process.ImageProcessor;
+import ij.plugin.filter.Analyzer;
+import ij.measure.ResultsTable;
 
 /** An Overlay is a list of ROIs that can be drawn non-destructively on an Image. */
 public class Overlay {
@@ -151,6 +153,21 @@ public class Overlay {
 			}
 		}
 	}
+	
+	/** Measures the ROIs in this overlay on the specified image
+	* and returns the results as a ResultsTable.
+	*/
+	public ResultsTable measure(ImagePlus imp) {
+		ResultsTable rt = new ResultsTable();
+		for (int i=0; i<size(); i++) {
+			Roi roi = get(i);
+			imp.setRoi(roi);
+			Analyzer analyzer = new Analyzer(imp, rt);
+			analyzer.measure();
+		}
+		imp.deleteRoi();
+		return rt;
+	}
 
 	/*
 	* Duplicate the elements of this overlay which  
diff --git a/ij/gui/Plot.java b/ij/gui/Plot.java
index 0abe1d2..b605b76 100644
--- a/ij/gui/Plot.java
+++ b/ij/gui/Plot.java
@@ -1880,7 +1880,7 @@ public class Plot implements Cloneable {
 				}
 			}
 			boolean haveMinorLogNumbers = i2-i1 < 2;		//nunbers on log minor ticks only if < 2 decades
-			if (minorTicks	&& (!logYAxis || step > 1.1)) { //'standard' log minor ticks only for full decades
+			if (yMin!=yMax && minorTicks	&& (!logYAxis || step > 1.1)) { //'standard' log minor ticks only for full decades
 				step = niceNumber(step*0.19);				//non-log: 4 or 5 minor ticks per major tick
 				if (logYAxis && step < 1) step = 1;
 				i1 = (int)Math.ceil (Math.min(yMin,yMax)/step-1.e-10);
diff --git a/ij/gui/PlotWindow.java b/ij/gui/PlotWindow.java
index 8df48ce..182d281 100644
--- a/ij/gui/PlotWindow.java
+++ b/ij/gui/PlotWindow.java
@@ -18,7 +18,7 @@ import ij.io.SaveDialog;
 * @author Michael Schmid
 * @author Wayne Rasband
 */
-public class PlotWindow extends ImageWindow implements ActionListener,	ItemListener,
+public class PlotWindow extends ImageWindow implements ActionListener, ItemListener,
 	ClipboardOwner, ImageListener, RoiListener, Runnable {
 
 	/** Display points using a circle 5 pixels in diameter. */
diff --git a/ij/gui/PointRoi.java b/ij/gui/PointRoi.java
index 887851a..d9ed01c 100644
--- a/ij/gui/PointRoi.java
+++ b/ij/gui/PointRoi.java
@@ -691,12 +691,14 @@ public class PointRoi extends PolygonRoi {
 		return handle;
 	}
 	
-	/** Returns the points as an array of Points. */
+	/** Returns the points as an array of Points. 
+	 * Wilhelm Burger: modified to use FloatPolygon for correct point positions.
+	*/
 	public Point[] getContainedPoints() {
-		Polygon p = getPolygon();
+		FloatPolygon p = getFloatPolygon();
 		Point[] points = new Point[p.npoints];
 		for (int i=0; i<p.npoints; i++)
-			points[i] = new Point(p.xpoints[i],p.ypoints[i]);
+			points[i] = new Point((int) Math.round(p.xpoints[i] - 0.5f), (int) Math.round(p.ypoints[i] - 0.5f));
 		return points;
 	}
 
@@ -708,7 +710,7 @@ public class PointRoi extends PolygonRoi {
 	/**
 	 * Custom iterator for points contained in a {@link PointRoi}.
 	 * @author W. Burger
-	 */
+	*/
 	public Iterator<Point> iterator() {	
 		return new Iterator<Point>() {
 			final Point[] pnts = getContainedPoints();
diff --git a/ij/gui/PolygonRoi.java b/ij/gui/PolygonRoi.java
index 94c8cd1..dc0ef36 100644
--- a/ij/gui/PolygonRoi.java
+++ b/ij/gui/PolygonRoi.java
@@ -150,8 +150,6 @@ public class PolygonRoi extends Roi {
 					subPixel = true;
 				break;
 		}
-		if (this instanceof EllipseRoi)
-			subPixel = true;
 		x = ic.offScreenX(sx);
 		y = ic.offScreenY(sy);
 		startXD = subPixelResolution()?ic.offScreenXD(sx):x;
@@ -361,7 +359,6 @@ public class PolygonRoi extends Roi {
 					xp2[i] = ic.screenXD(xpf[i]+xbase+offset);
 					yp2[i] = ic.screenYD(ypf[i]+ybase+offset);
 				}
-				//IJ.log(xp2[0]+" "+xpf[0]+" "+xbase+" "+offset+" "+bounds);
 			} else {
 				for (int i=0; i<nPoints; i++) {
 					xp2[i] = ic.screenX(xp[i]+x);
@@ -767,10 +764,10 @@ public class PolygonRoi extends Roi {
 			return;
 		int ox=ic.offScreenX(sx), oy=ic.offScreenY(sy);
 		double oxd=ic.offScreenXD(sx), oyd=ic.offScreenYD(sy);
-		if ((IJ.altKeyDown()||IJ.controlKeyDown()) && !(nPoints<=3 && type!=POINT)) {
+		if ((IJ.altKeyDown()||IJ.controlKeyDown()) && !(nPoints<=3 && type!=POINT) && !(this instanceof RotatedRectRoi)) {
 			deleteHandle(oxd, oyd); 
 			return;
-		} else if (IJ.shiftKeyDown() && type!=POINT) {
+		} else if (IJ.shiftKeyDown() && type!=POINT && !(this instanceof RotatedRectRoi)) {
 			addHandle(oxd, oyd); 
 			return;
 		}
diff --git a/ij/gui/ProgressBar.java b/ij/gui/ProgressBar.java
index 63ebff1..6cf9969 100644
--- a/ij/gui/ProgressBar.java
+++ b/ij/gui/ProgressBar.java
@@ -11,6 +11,9 @@ import java.awt.image.*;
  */
 public class ProgressBar extends Canvas {
 
+    public static final int WIDTH = 120;
+    public static final int HEIGHT = 20;
+
     private int canvasWidth, canvasHeight;
     private int x, y, width, height;
     private long lastTime = 0;
diff --git a/ij/gui/Roi.java b/ij/gui/Roi.java
index 974d62d..9016006 100644
--- a/ij/gui/Roi.java
+++ b/ij/gui/Roi.java
@@ -81,6 +81,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	private String name;
 	private int position;
 	private int channel, slice, frame;
+	private boolean hyperstackPosition;
 	private Overlay prototypeOverlay;
 	private boolean subPixel;
 	private boolean activeOverlayRoi;
@@ -544,7 +545,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	
 	/** Returns the coordinates of the pixels inside this ROI as an array of Points.
 	 * @see #getContainedFloatPoints()
-	 * @see #Iterator()
+	 * @see #iterator()
 	 */
 	public Point[] getContainedPoints() {
 		if (isLine()) {
@@ -1013,18 +1014,22 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 			case KeyEvent.VK_UP:
 				height--;
 				if (height<1) height = 1;
+				notifyListeners(RoiListener.MODIFIED);
 				break;
 			case KeyEvent.VK_DOWN:
 				height++;
 				if ((y+height) > yMax) height = yMax-y;
+				notifyListeners(RoiListener.MODIFIED);
 				break;
 			case KeyEvent.VK_LEFT:
 				width--;
 				if (width<1) width = 1;
+				notifyListeners(RoiListener.MODIFIED);
 				break;
 			case KeyEvent.VK_RIGHT:
 				width++;
 				if ((x+width) > xMax) width = xMax-x;
+				notifyListeners(RoiListener.MODIFIED);
 				break;
 		}
 		updateClipRect();
@@ -1683,6 +1688,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 		if (n<0) n=0;
 		position = n;
 		channel = slice = frame = 0;
+		hyperstackPosition = false;
 	} 
 
 	/** Returns the stack position (image number) of this ROI, or
@@ -1705,8 +1711,27 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 		if (frame<0) frame=0;
 		this.frame = frame;
 		position = 0;
+		hyperstackPosition = true;
+	}
+	
+	/** Returns 'true' if setPosition(C,Z,T) has been called. */
+	public boolean hasHyperStackPosition() {
+		return hyperstackPosition;
 	}
 	
+	/** Sets the position of this ROI based on the stack position of the specified image.  */
+	public void setPosition(ImagePlus imp ) {
+		if (imp==null)
+			return;
+		if (imp.isHyperStack()) {
+			int channel = imp.getDisplayMode()==IJ.COMPOSITE?0:imp.getChannel();
+			setPosition(channel, imp.getSlice(), imp.getFrame());
+		} else if (imp.getStackSize()>1)
+			setPosition(imp.getCurrentSlice());
+		else
+			setPosition(0);
+	}
+		
 	/** Returns the channel position of this ROI, or zero
 	*  if this ROI is not associated with a particular channel.
 	*/
@@ -1718,7 +1743,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	*  if this ROI is not associated with a particular slice.
 	*/
 	public final int getZPosition() {
-		return slice;
+		return slice==0&&!hyperstackPosition?position:slice;
 	}
 	
 	/** Returns the frame position of this ROI, or zero
@@ -1875,7 +1900,6 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 			temp = new int[n];
 		for (int i=0; i<size; i++)
 			temp[i] = (int)arr[i];
-			//temp[i] = (int)Math.floor(arr[i]+0.5);
 		return temp;
 	}
 
@@ -2024,20 +2048,27 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	}
 	
 	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);
+		Roi roi = this;
+		ImageProcessor ip = null;
+		if (imp!=null)
+			ip = imp.getProcessor();
+		boolean noImage = ip==null;
+		Rectangle bounds = null;
+		if (noImage) {
+			roi = (Roi)this.clone();
+			bounds = roi.getBounds();
+			ip = new ByteProcessor(bounds.width, bounds.height);
+			roi.setLocation(0, 0);
+		}
+		if (roi.isLine())
+			roi = null;
 		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;
+		ImageStatistics stats = ip.getStatistics();
+		if (noImage) {
+			stats.mean = stats.min = stats.max = Double.NaN;
+			stats.xCentroid+=bounds.x; stats.yCentroid+=bounds.y; 
+		}
+		ip.resetRoi();
 		return stats;
 	}
 
@@ -2098,7 +2129,7 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 			}
 		}
 	}
-
+	
 	public static void addRoiListener(RoiListener listener) {
 		listeners.addElement(listener);
 	}
@@ -2118,53 +2149,18 @@ public class Roi extends Object implements Cloneable, java.io.Serializable, Iter
 	 * @see #getContainedPoints()
 	 * @see #getContainedFloatPoints()
 	 * @author Wilhelm Burger
-	 */
+	*/
 	public Iterator<Point> iterator() {
-		if (isLine() && getStrokeWidth()<=1.0)
-			return new RoiPointsIteratorLine();
-		else
-			return new RoiPointsIteratorMask();
+		// Returns the default (mask-based) point iterator. Note that 'Line' overrides the 
+		// iterator() method and returns a specific point iterator.
+		return new RoiPointsIteratorMask();
 	}
 	
-
-	/**
-	 * Custom iterator over points contained in a straight line-type {@link Roi}.
-	 * @author W. Burger
-	 */
-	private class RoiPointsIteratorLine implements Iterator<Point> {
-		private final FloatPolygon p;
-		private int next = 0;
-
-		RoiPointsIteratorLine() {
-			p = getInterpolatedPolygon();
-		}
-
-		@Override
-		public boolean hasNext() {
-			return next<p.npoints;
-		}
-
-		@Override
-		public Point next() {
-			if (next >= p.npoints)
-				throw new NoSuchElementException();
-			int x = (int)Math.round(p.xpoints[next]);
-			int y = (int)Math.round(p.ypoints[next]);
-			next = next + 1;
-			return new Point(x, y);
-		}
-		
-		@Override
-		public void remove() {
-			throw new UnsupportedOperationException();
-		}
-
-	}
 	
 	/**
-	 * Custom iterator over points contained in a mask-backed {@link Roi}.
-	 * @author W. Burger
-	 */
+	 * Default iterator over points contained in a mask-backed {@link Roi}.
+	 * Author: W. Burger
+	*/
 	private class RoiPointsIteratorMask implements Iterator<Point> {
 		private final ImageProcessor mask;
 		private final Rectangle bounds;
diff --git a/ij/gui/RoiProperties.java b/ij/gui/RoiProperties.java
index 6aad86d..eccbd3f 100644
--- a/ij/gui/RoiProperties.java
+++ b/ij/gui/RoiProperties.java
@@ -84,11 +84,8 @@ public class RoiProperties {
 			antialias = troi.getAntialiased();
 		}
 		String position = ""+roi.getPosition();
-		int cpos = roi.getCPosition();
-		int zpos = roi.getZPosition();
-		int tpos = roi.getTPosition();
-		if (cpos>0 || zpos>0 || tpos>0)
-			position = cpos +","+zpos+","+tpos;
+		if (roi.hasHyperStackPosition())
+			position =  roi.getCPosition() +","+roi.getZPosition()+","+ roi.getTPosition();
 		if (position.equals("0"))
 			position = "none";
 		String linec = Colors.colorToString(strokeColor);
@@ -253,6 +250,7 @@ public class RoiProperties {
 				rois[i].setFillColor(fillColor);
 			}
 			imp.draw();
+			imp.getProcessor(); // needed for corect recordering
 		}
 		if (listCoordinates) {
 			if (showPointCounts && (roi instanceof PointRoi))
diff --git a/ij/gui/RotatedRectRoi.java b/ij/gui/RotatedRectRoi.java
new file mode 100644
index 0000000..f7147c9
--- /dev/null
+++ b/ij/gui/RotatedRectRoi.java
@@ -0,0 +1,176 @@
+package ij.gui;
+import java.awt.*;
+import java.awt.image.*;
+import ij.*;
+import ij.plugin.frame.Recorder;
+import ij.process.FloatPolygon;
+import ij.measure.Calibration;
+
+/** This class implements the rotated rectangle selection tool. */
+public class RotatedRectRoi extends PolygonRoi {
+	private double xstart, ystart;
+	private static double DefaultRectWidth = 50;
+	private double rectWidth = DefaultRectWidth;
+
+	public RotatedRectRoi(double x1, double y1, double x2, double y2, double rectWidth) {
+		super(new float[5], new float[5], 5, FREEROI);
+		this.rectWidth = rectWidth;
+		makeRectangle(x1, y1, x2, y2);
+		state = NORMAL;
+		bounds = null;
+	}
+
+	public RotatedRectRoi(int sx, int sy, ImagePlus imp) {
+		super(sx, sy, imp);
+		type = FREEROI;
+		xstart = ic.offScreenXD(sx);
+		ystart = ic.offScreenYD(sy);
+		ImageWindow win = imp.getWindow();
+		int pixels = win!=null?(int)(win.getSize().height/win.getCanvas().getMagnification()):imp.getHeight();
+		if (rectWidth>pixels)
+			rectWidth = pixels/3;
+		setDrawOffset(false);
+		bounds = null;
+	}
+
+	public void draw(Graphics g) {
+		super.draw(g);
+		if (!overlay && ic!=null) {
+			double mag = ic.getMagnification();
+		    int size2 = HANDLE_SIZE/2;
+			for (int i=0; i<4; i++)
+				drawHandle(g, hxs(i)-size2, hys(i)-size2);
+		}
+	}
+	
+	private int hxs(int index) {
+		int indexPlus1 = index<3?index+1:0;
+		return xp2[index]+(xp2[indexPlus1]-xp2[index])/2;
+	}
+
+	private int hys(int index) {
+		int indexPlus1 = index<3?index+1:0;
+		return yp2[index]+(yp2[indexPlus1]-yp2[index])/2;
+	}
+
+	private double hx(int index) {
+		int indexPlus1 = index<3?index+1:0;
+		return xpf[index]+(xpf[indexPlus1]-xpf[index])/2+x;
+	}
+
+	private double hy(int index) {
+		int indexPlus1 = index<3?index+1:0;
+		return ypf[index]+(ypf[indexPlus1]-ypf[index])/2+y;
+	}
+
+	protected void grow(int sx, int sy) {
+		double x1 = xstart;
+		double y1 = ystart;
+		double x2 = ic.offScreenXD(sx);
+		double y2 = ic.offScreenYD(sy);
+		makeRectangle(x1, y1, x2, y2);
+		imp.draw();
+	}
+		
+	void makeRectangle(double x1, double y1, double x2, double y2) {
+		double length = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
+		double angle = Math.atan ((x2-x1)/(y2-y1));
+		double wsa = (rectWidth/2.0)*Math.sin((Math.PI/2.0)+angle);
+		double wca = (rectWidth/2.0)*Math.cos((Math.PI/2)+angle);
+		nPoints = 5;
+		xpf[3] = (float)(x1-wsa);
+		ypf[3] = (float)(y1-wca);
+		xpf[0] = (float)(x1+wsa);
+		ypf[0] = (float)(y1+wca);
+		xpf[1] = (float)(x2+wsa);
+		ypf[1] = (float)(y2+wca);
+		xpf[2] = (float)(x2-wsa);
+		ypf[2] = (float)(y2-wca);
+		xpf[4] = xpf[0];
+		ypf[4] = ypf[0];
+		makePolygonRelative();
+		cachedMask = null;
+		DefaultRectWidth = rectWidth;
+	}
+
+	void makePolygonRelative() {
+		FloatPolygon poly = new FloatPolygon(xpf, ypf, nPoints);
+		Rectangle r = poly.getBounds();
+		x = r.x;
+		y = r.y;
+		width = r.width;
+		height = r.height;
+		bounds = null;
+		for (int i=0; i<nPoints; i++) {
+			xpf[i] = xpf[i]-x;
+			ypf[i] = ypf[i]-y;
+		}
+	}
+	
+	protected void handleMouseUp(int screenX, int screenY) {
+		nPoints = 4;
+		state = NORMAL;
+	}
+	
+	protected void moveHandle(int sx, int sy) {
+		double ox = ic.offScreenXD(sx); 
+		double oy = ic.offScreenYD(sy);
+		double x1 = hx(3);
+		double y1 = hy(3);
+		double x2 = hx(1);
+		double y2 = hy(1);
+		switch(activeHandle) {
+			case 0: 
+				double dx = hx(2) - ox;
+				double dy = hy(2) - oy;
+				rectWidth = Math.sqrt(dx*dx+dy*dy);
+				break;
+			case 1: 
+				x2 = ox;
+				y2 = oy;
+				break;
+			case 2: 
+				dx = hx(0) - ox;
+				dy = hy(0) - oy;
+				rectWidth = Math.sqrt(dx*dx+dy*dy);
+				break;
+			case 3: 
+				x1 = ox;
+				y1 = oy;
+				break;
+		}
+		makeRectangle(x1, y1, x2, y2);
+		imp.draw();
+	}
+	
+	public int isHandle(int sx, int sy) {
+		int size = HANDLE_SIZE+5;
+		int halfSize = size/2;
+		int index = -1;
+		for (int i=0; i<4; i++) {
+			int sx2 = (int)Math.round(hxs(i)-halfSize), sy2=(int)Math.round(hys(i)-halfSize);
+			if (sx>=sx2 && sx<=sx2+size && sy>=sy2 && sy<=sy2+size) {
+				index = i;
+				break;
+			}
+		}
+		return index;
+	}
+	
+	/** Returns x1, y1, x2, y2 and width as a 5 element array. */
+	public double[] getParams() {
+		double[] params = new double[5];
+		params[0] = hx(3);
+		params[1]  = hy(3);
+		params[2]  = hx(1);
+		params[3]  = hy(1);
+		params[4]  = rectWidth;
+		return params;
+	}
+	
+	/** Always returns true. */
+	public boolean subPixelResolution() {
+		return true;
+	}
+
+}
diff --git a/ij/gui/ScrollbarWithLabel.java b/ij/gui/ScrollbarWithLabel.java
index a8ec84d..3f77d06 100644
--- a/ij/gui/ScrollbarWithLabel.java
+++ b/ij/gui/ScrollbarWithLabel.java
@@ -22,7 +22,6 @@ public class ScrollbarWithLabel extends Panel implements Adjustable, AdjustmentL
 		super(new BorderLayout(2, 0));
 		this.stackWindow = stackWindow;
 		bar = new Scrollbar(Scrollbar.HORIZONTAL, value, visible, minimum, maximum);
-		GUI.fix(bar);
 		icon = new Icon(label);
 		add(icon, BorderLayout.WEST);
 		add(bar, BorderLayout.CENTER);
diff --git a/ij/gui/Toolbar.java b/ij/gui/Toolbar.java
index 616b343..6613cde 100644
--- a/ij/gui/Toolbar.java
+++ b/ij/gui/Toolbar.java
@@ -5,7 +5,7 @@ import java.awt.event.*;
 import java.io.File;
 import java.util.*;
 import ij.*;
-import ij.plugin.frame.Recorder; 
+import ij.plugin.frame.Recorder;
 import ij.plugin.frame.Editor; 
 import ij.plugin.MacroInstaller;
 import ij.plugin.RectToolOptions;
@@ -41,9 +41,10 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	
 	public static final int DOUBLE_CLICK_THRESHOLD = 650;
 
+	public static final int RECT_ROI=0, ROUNDED_RECT_ROI=1, ROTATED_RECT_ROI=2;
 	public static final int OVAL_ROI=0, ELLIPSE_ROI=1, BRUSH_ROI=2;
 	
-	private static final String[] builtInTools = {"Arrow","Brush","Developer Menu","Flood Filler",
+	private static final String[] builtInTools = {"Arrow","Brush","Command Finder", "Developer Menu","Flood Filler",
 		"LUT Menu","Overlay Brush","Pencil","Pixel Inspector","Selection Rotator", "Spray Can","Stacks Menu"};
 	private static final String[] builtInTools2 = {"Pixel Inspection Tool","Paintbrush Tool","Flood Fill Tool"};
 
@@ -51,14 +52,16 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	private static final int MAX_EXTRA_TOOLS = 8;
 	private static final int MAX_TOOLS = NUM_TOOLS+MAX_EXTRA_TOOLS;
 	private static final int NUM_BUTTONS = 21;
-	private static final int SIZE = 28;
+	private static final int BUTTON_WIDTH = 28;
+	private static final int BUTTON_HEIGHT = 29;
+	private static final int SIZE = 28;  // no longer used
 	private static final int GAP_SIZE = 9;
 	private static final int OFFSET = 6;
 	private static final String BRUSH_SIZE = "toolbar.brush.size";
 	public static final String CORNER_DIAMETER = "toolbar.arc.size";
 	public static String TOOL_KEY = "toolbar.tool";
 		
-	private Dimension ps = new Dimension(SIZE*NUM_BUTTONS-(SIZE-GAP_SIZE), SIZE);
+	private Dimension ps = new Dimension(BUTTON_WIDTH*NUM_BUTTONS-(BUTTON_WIDTH-GAP_SIZE), BUTTON_HEIGHT);
 	private boolean[] down;
 	private static int current;
 	private int previous;
@@ -81,7 +84,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	private String icon;
 	private int startupTime;
 	private PopupMenu rectPopup, ovalPopup, pointPopup, linePopup, switchPopup;
-	private CheckboxMenuItem rectItem, roundRectItem;
+	private CheckboxMenuItem rectItem, roundRectItem, rotatedRectItem;
 	private CheckboxMenuItem ovalItem, ellipseItem, brushItem;
 	private CheckboxMenuItem pointItem, multiPointItem;
 	private CheckboxMenuItem straightLineItem, polyLineItem, freeLineItem, arrowItem;
@@ -90,13 +93,14 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	private static Color foregroundColor = Prefs.getColor(Prefs.FCOLOR,Color.white);
 	private static Color backgroundColor = Prefs.getColor(Prefs.BCOLOR,Color.black);
 	private static int ovalType = OVAL_ROI;
+	private static int rectType = RECT_ROI;
 	private static boolean multiPointMode = Prefs.multiPointMode;
-	private static boolean roundRectMode;
 	private static boolean arrowMode;
 	private static int brushSize = (int)Prefs.get(BRUSH_SIZE, 15);
 	private static int arcSize = (int)Prefs.get(CORNER_DIAMETER, 20);
 	private int lineType = LINE;
 	private static boolean legacyMode;
+	//private static BasicStroke widerLine = new BasicStroke(1.5f);
 	
 	private Color gray = new Color(228,228,228);
 	private Color brighter = gray.brighter();
@@ -120,7 +124,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		addMouseMotionListener(this);
 		instance = this;
 		names[getNumTools()-1] = "\"More Tools\" menu (switch toolsets or add tools)";
-		icons[getNumTools()-1] = "C900T1c13>T7c13>"; // ">>"
+		icons[getNumTools()-1] = "C900T1e15>T7e15>"; // ">>"
 		addPopupMenus();
 	}
 
@@ -128,12 +132,15 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		rectPopup = new PopupMenu();
 		if (Menus.getFontSize()!=0)
 			rectPopup.setFont(Menus.getFont());
-		rectItem = new CheckboxMenuItem("Rectangle Tool", !roundRectMode);
+		rectItem = new CheckboxMenuItem("Rectangle", rectType==RECT_ROI);
 		rectItem.addItemListener(this);
 		rectPopup.add(rectItem);
-		roundRectItem = new CheckboxMenuItem("Rounded Rectangle Tool", roundRectMode);
+		roundRectItem = new CheckboxMenuItem("Rounded Rectangle", rectType==ROUNDED_RECT_ROI);
 		roundRectItem.addItemListener(this);
 		rectPopup.add(roundRectItem);
+		rotatedRectItem = new CheckboxMenuItem("Rotated Rectangle", rectType==ROTATED_RECT_ROI);
+		rotatedRectItem.addItemListener(this);
+		rectPopup.add(rotatedRectItem);
 		add(rectPopup);
 
 		ovalPopup = new PopupMenu();
@@ -221,6 +228,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			Graphics2D g2d = (Graphics2D)g;
 			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 			g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+			//g2d.setStroke(widerLine);
 		}
 		for (int i=0; i<LINE; i++)
 			drawButton(g, i);
@@ -255,29 +263,31 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 				return;
 		}
         int index = toolIndex(tool);
-        int x = index*SIZE + 1;
+        int x = index*BUTTON_WIDTH + 1;
         if (tool>=CUSTOM1)
-        	x -= SIZE-GAP_SIZE;
+        	x -= BUTTON_WIDTH-GAP_SIZE;
         if (tool!=UNUSED)
-        	fill3DRect(g, x, 1, SIZE, SIZE-1, !down[tool]);
+        	fill3DRect(g, x, 1, BUTTON_WIDTH, BUTTON_HEIGHT-1, !down[tool]);
         g.setColor(toolColor);
-        x = index*SIZE + OFFSET;
+        x = index*BUTTON_WIDTH + OFFSET;
         if (tool>=CUSTOM1)
-        	x -= SIZE-GAP_SIZE;
+        	x -= BUTTON_WIDTH-GAP_SIZE;
 		int y = OFFSET;
 		if (down[tool]) { x++; y++;}
 		this.g = g;
 		if (tool>=CUSTOM1 && tool<=getNumTools() && icons[tool]!=null) {
-			drawIcon(g, tool, x, y);
+			drawIcon(g, tool, x+1, y+1);
 			return;
 		}
 		switch (tool) {
 			case RECTANGLE:
 				xOffset = x; yOffset = y;
-				if (roundRectMode)
-					g.drawRoundRect(x, y+1, 17, 13, 8, 8);
+				if (rectType==ROUNDED_RECT_ROI)
+					g.drawRoundRect(x-1, y+1, 17, 13, 8, 8);
+				else if (rectType==ROTATED_RECT_ROI)
+					polyline(0,10,7,0,15,6,8,16,0,10); 
 				else
-					g.drawRect(x, y+1, 17, 13);
+					g.drawRect(x-1, y+1, 17, 13);
 				drawTriangle(16,15);
 				return;
 			case OVAL:
@@ -286,6 +296,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 					yOffset = y - 1;
 					polyline(6,4,8,2,12,1,15,2,16,4,15,7,12,8,9,11,9,14,6,16,2,16,0,13,1,10,4,9,6,7,6,4);
 				} else if (ovalType==ELLIPSE_ROI) {
+					xOffset = x - 1;
 					yOffset = y + 1;
 					polyline(11,0,13,0,14,1,15,1,16,2,17,3,17,7,12,12,11,12,10,13,8,13,7,14,4,14,3,13,2,13,1,12,1,11,0,10,0,9,1,8,1,7,6,2,7,2,8,1,10,1,11,0);
 				} else
@@ -294,10 +305,10 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 				return;
 			case POLYGON:
 				xOffset = x+1; yOffset = y+2;
-				polyline(4,0,15,0,15,1,11,5,11,6,14,10,14,11,0,11,0,4,4,0);
+				polyline(4,0,15,0,15,1,11,5,11,6,14,11,14,12,0,12,0,4,4,0);
 				return;
 			case FREEROI:
-				xOffset = x; yOffset = y+2;
+				xOffset = x; yOffset = y+3;
 				polyline(2,0,5,0,7,3,10,3,12,0,15,0,17,2,17,5,16,8,13,10,11,11,6,11,4,10,1,8,0,6,0,2,2,0); 
 				return;
 			case LINE:
@@ -308,17 +319,17 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 					m(0,12); d(17,3);
 					drawDot(0,11); drawDot(17,2);
 				}
-				drawTriangle(12,14);
+				drawTriangle(15,15);
 				return;
 			case POLYLINE:
 				xOffset = x; yOffset = y;
 				polyline(15,6,11,2,1,2,1,3,7,9,2,14);
-				drawTriangle(12,14);
+				drawTriangle(13,15);
 				return;
 			case FREELINE:
 				xOffset = x; yOffset = y;
 				polyline(16,4,14,6,12,6,9,3,8,3,6,7,2,11,1,11);
-				drawTriangle(12,14);
+				drawTriangle(13,15);
 				return;
 			case POINT:
 				xOffset = x; yOffset = y;
@@ -332,7 +343,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 					g.setColor(Roi.getColor());
 					g.fillRect(x+7, y+7, 3, 3);
 				}
-				drawTriangle(14,14);
+				drawTriangle(15,15);
 				return;
 			case WAND:
 				xOffset = x+2; yOffset = y+1;
@@ -370,7 +381,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 				polyline(10,7,12,7,12,9);
 				polyline(8,7,2,13,2,15,4,15,11,8);
 				g.setColor(backgroundColor);
-				polyline(-1,-1,18,-1,18,17,-1,17,-1,-1);
+				polyline(-1,-1,18,-1,18,18,-1,18,-1,-1);
 				return;
 			case ANGLE:
 				xOffset = x; yOffset = y+2;
@@ -463,7 +474,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		}
 		if (menus[tool]!=null && menus[tool].getItemCount()>0) { 
 			xOffset = x; yOffset = y;
-			drawTriangle(14, 14);
+			drawTriangle(15, 15);
 		}
 	}
 	
@@ -511,10 +522,12 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		String hint2 = " (right click to switch; double click to configure)";
 		switch (tool) {
 			case RECTANGLE:
-				if (roundRectMode)
-					IJ.showStatus("Rectangular or *rounded rectangular* selections"+hint);
+				if (rectType==ROUNDED_RECT_ROI)
+					IJ.showStatus("Rectangle, *rounded rect* or rotated rect"+hint);
+				else if (rectType==ROTATED_RECT_ROI)
+					IJ.showStatus("Rectangle, rounded rect or *rotated rect*"+hint);
 				else
-					IJ.showStatus("*Rectangular* or rounded rectangular selections"+hint);
+					IJ.showStatus("*Rectangle*, rounded rect or rotated rect"+hint);
 				return;
 			case OVAL:
 				if (ovalType==BRUSH_ROI)
@@ -621,10 +634,13 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		name = name.toLowerCase(Locale.US);
 		boolean ok = true;
 		if (name.indexOf("round")!=-1) {
-			roundRectMode = true;
+			rectType = ROUNDED_RECT_ROI;
+			setTool(RECTANGLE);
+		} else if (name.indexOf("rot")!=-1) {
+			rectType = ROTATED_RECT_ROI;
 			setTool(RECTANGLE);
 		} else if (name.indexOf("rect")!=-1) {
-			roundRectMode = false;
+			rectType = RECT_ROI;
 			setTool(RECTANGLE);
 		} else if (name.indexOf("oval")!=-1) {
 			ovalType = OVAL_ROI;
@@ -685,7 +701,12 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	/** Returns the name of the specified tool. */
 	String getName(int id) {
 		switch (id) {
-			case RECTANGLE: return roundRectMode?"roundrect":"rectangle";
+			case RECTANGLE:
+				switch (rectType) {
+					case RECT_ROI: return "rectangle";
+					case ROUNDED_RECT_ROI: return "roundrect";
+					case ROTATED_RECT_ROI: return "rotrect";
+				}
 			case OVAL:
 				switch (ovalType) {
 					case OVAL_ROI: return "oval";
@@ -749,7 +770,10 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		previous = current;
 		if (Recorder.record) {
 			String name = getName(current);
-			if (name!=null) Recorder.record("setTool", name);
+			if (name!=null) {
+				IJ.wait(100); // workaround for OSX/Java 8 bug
+				Recorder.record("setTool", name);
+			}
 		}
 		if (legacyMode)
 			repaint();
@@ -842,16 +866,16 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		
 	/** Returns the rounded rectangle arc size, or 0 if the rounded rectangle tool is not enabled. */
 	public static int getRoundRectArcSize() {
-		if (!roundRectMode)
-			return 0;
-		else
+		if (rectType==ROUNDED_RECT_ROI)
 			return arcSize;
+		else
+			return 0;
 	}
 
 	/** Sets the rounded rectangle corner diameter (pixels). */
 	public static void setRoundRectArcSize(int size) {
 		if (size<=0)
-			roundRectMode = false;
+			rectType = RECT_ROI;
 		else {
 			arcSize = size;
 			Prefs.set(CORNER_DIAMETER, arcSize);
@@ -860,7 +884,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		ImagePlus imp = WindowManager.getCurrentImage();
 		Roi roi = imp!=null?imp.getRoi():null;
 		if (roi!=null && roi.getType()==Roi.RECTANGLE)
-			roi.setCornerDiameter(roundRectMode?arcSize:0);
+			roi.setCornerDiameter(rectType==ROUNDED_RECT_ROI?arcSize:0);
 	}
 
 	/** Returns 'true' if the multi-point tool is enabled. */
@@ -868,13 +892,19 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 		return multiPointMode;
 	}
 
+	/** Returns the rectangle tool type (RECT_ROI, ROUNDED_RECT_ROI or ROTATED_RECT_ROI). */
+	public static int getRectToolType() {
+		return rectType;
+	}
+
 	/** Returns the oval tool type (OVAL_ROI, ELLIPSE_ROI or BRUSH_ROI). */
 	public static int getOvalToolType() {
 		return ovalType;
 	}
 
+	/** Returns the button width (button spacing). */
 	public static int getButtonSize() {
-		return SIZE;
+		return BUTTON_WIDTH;
 	}
 	
 	static void repaintTool(int tool) {
@@ -888,8 +918,6 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			tb.drawButton(g, tool);
 			if (g!=null) g.dispose();
 		}
-		//Toolbar tb = getInstance();
-		//tb.repaint(tool * SIZE , 0, SIZE, SIZE);
 	}
 	
 	// Returns the toolbar position index of the specified tool
@@ -916,9 +944,9 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 
 	// Returns the tool corresponding to the specified x coordinate
 	private int toolID(int x) {
-		if (x>SIZE*12+GAP_SIZE)
+		if (x>BUTTON_WIDTH*12+GAP_SIZE)
 			x -= GAP_SIZE;
-		int index = x/SIZE;
+		int index = x/BUTTON_WIDTH;
     	switch (index) {
 			case 0: return RECTANGLE;
 			case 1: return OVAL;
@@ -937,7 +965,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
     }
     
 	private boolean inGap(int x) {
-		return x>=(SIZE*12) && x<(SIZE*12+GAP_SIZE);
+		return x>=(BUTTON_WIDTH*12) && x<(BUTTON_WIDTH*12+GAP_SIZE);
  	}
 
 	public void mousePressed(MouseEvent e) {
@@ -982,8 +1010,9 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			setTool2(newTool);
 			boolean isRightClick = e.isPopupTrigger()||e.isMetaDown();
 			if (current==RECTANGLE && isRightClick) {
-				rectItem.setState(!roundRectMode);
-				roundRectItem.setState(roundRectMode);
+				rectItem.setState(rectType==RECT_ROI);
+				roundRectItem.setState(rectType==ROUNDED_RECT_ROI);
+				rotatedRectItem.setState(rectType==ROTATED_RECT_ROI);
 				if (IJ.isMacOSX()) IJ.wait(10);
 				rectPopup.show(e.getComponent(),x,y);
 				mouseDownTime = 0L;
@@ -1032,7 +1061,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			ImagePlus imp = WindowManager.getCurrentImage();
 			switch (current) {
 				case RECTANGLE:
-					if (roundRectMode)
+					if (rectType==ROUNDED_RECT_ROI)
 						IJ.doCommand("Rounded Rect Tool...");
 					break;
 				case OVAL:
@@ -1210,14 +1239,19 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 	public void itemStateChanged(ItemEvent e) {
 		CheckboxMenuItem item = (CheckboxMenuItem)e.getSource();
 		String previousName = getToolName();
-		if (item==rectItem || item==roundRectItem) {
-			roundRectMode = item==roundRectItem;
+		if (item==rectItem || item==roundRectItem || item==rotatedRectItem) {
+			if (item==roundRectItem)
+				rectType = ROUNDED_RECT_ROI;
+			else if (item==rotatedRectItem)
+				rectType = ROTATED_RECT_ROI;
+			else
+				rectType = RECT_ROI;
 			repaintTool(RECTANGLE);
 			showMessage(RECTANGLE);
 			ImagePlus imp = WindowManager.getCurrentImage();
 			Roi roi = imp!=null?imp.getRoi():null;
 			if (roi!=null && roi.getType()==Roi.RECTANGLE)
-				roi.setCornerDiameter(roundRectMode?arcSize:0);
+				roi.setCornerDiameter(rectType==ROUNDED_RECT_ROI?arcSize:0);
 			if (!previousName.equals(getToolName()))
 				IJ.notifyEventListeners(IJEventListener.TOOL_CHANGED);
 		} else if (item==ovalItem || item==ellipseItem || item==brushItem) {
@@ -1341,7 +1375,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			nExtraTools = 0;
 			names[getNumTools()-1] = name;
 			icons[getNumTools()-1] = icon;
-			ps = new Dimension(SIZE*NUM_BUTTONS-(SIZE-GAP_SIZE)+nExtraTools*SIZE, SIZE);
+			ps = new Dimension(BUTTON_WIDTH*NUM_BUTTONS-(BUTTON_WIDTH-GAP_SIZE)+nExtraTools*BUTTON_WIDTH, BUTTON_HEIGHT);
 			IJ.getInstance().pack();
 		}
 	}
@@ -1442,7 +1476,7 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			icons[getNumTools()-1] = icons[getNumTools()-2];
 			names[getNumTools()-2] = null;
 			icons[getNumTools()-2] = null;
-			ps = new Dimension(SIZE*NUM_BUTTONS-(SIZE-GAP_SIZE)+nExtraTools*SIZE, SIZE);
+			ps = new Dimension(BUTTON_WIDTH*NUM_BUTTONS-(BUTTON_WIDTH-GAP_SIZE)+nExtraTools*BUTTON_WIDTH, BUTTON_HEIGHT);
 			IJ.getInstance().pack();
 			tool = getNumTools()-2;
 		}
@@ -1736,6 +1770,8 @@ public class Toolbar extends Canvas implements MouseListener, MouseMotionListene
 			(new MacroInstaller()).installFromIJJar("/macros/StacksMenuTool.txt");
 		} else if (label.startsWith("LUT Menu")) {
 			(new MacroInstaller()).installFromIJJar("/macros/LUTMenuTool.txt");
+		} else if (label.startsWith("Command Finder")) {
+			(new MacroInstaller()).installFromIJJar("/macros/CommandFinderTool.txt");
 		} else
 			ok = false;
 		return ok;
diff --git a/ij/gui/YesNoCancelDialog.java b/ij/gui/YesNoCancelDialog.java
index 8088225..2a7c273 100644
--- a/ij/gui/YesNoCancelDialog.java
+++ b/ij/gui/YesNoCancelDialog.java
@@ -11,6 +11,10 @@ public class YesNoCancelDialog extends Dialog implements ActionListener, KeyList
 	private boolean firstPaint = true;
 
 	public YesNoCancelDialog(Frame parent, String title, String msg) {
+		this(parent, title, msg, "  Yes  ", "  No  ");
+	}
+
+	public YesNoCancelDialog(Frame parent, String title, String msg, String yesLabel, String noLabel) {
 		super(parent, title, true);
 		setLayout(new BorderLayout());
 		Panel panel = new Panel();
@@ -27,8 +31,8 @@ public class YesNoCancelDialog extends Dialog implements ActionListener, KeyList
 			noB = new Button("Don't Save");
 			cancelB = new Button("  Cancel  ");
 		} else {
-			yesB = new Button("  Yes  ");
-			noB = new Button("  No  ");
+			yesB = new Button(yesLabel);
+			noB = new Button(noLabel);
 			cancelB = new Button(" Cancel ");
 		}
 		yesB.addActionListener(this);
diff --git a/ij/io/FileOpener.java b/ij/io/FileOpener.java
index 8dec36c..fa17780 100644
--- a/ij/io/FileOpener.java
+++ b/ij/io/FileOpener.java
@@ -327,7 +327,7 @@ public class FileOpener {
 		Calibration cal = imp.getCalibration();
 		boolean calibrated = false;
 		if (fi.pixelWidth>0.0 && fi.unit!=null) {
-			if (fi.pixelWidth<=0.0001 && fi.unit.equals("cm")) {
+			if (Prefs.convertToMicrons && fi.pixelWidth<=0.0001 && fi.unit.equals("cm")) {
 				fi.pixelWidth *= 10000.0;
 				fi.pixelHeight *= 10000.0;
 				if (fi.pixelDepth!=1.0)
@@ -447,7 +447,7 @@ public class FileOpener {
 		else if (fi.url!=null && !fi.url.equals(""))
 			is = new URL(fi.url+fi.fileName).openStream();
 		else {
-			if (fi.directory.length()>0 && !fi.directory.endsWith(Prefs.separator))
+			if (fi.directory.length()>0 && !(fi.directory.endsWith(Prefs.separator)||fi.directory.endsWith("/")))
 				fi.directory += Prefs.separator;
 		    File f = new File(fi.directory + fi.fileName);
 		    if (gzip) fi.compression = FileInfo.COMPRESSION_UNKNOWN;
diff --git a/ij/io/ImageReader.java b/ij/io/ImageReader.java
index 624bbe6..cd7e656 100644
--- a/ij/io/ImageReader.java
+++ b/ij/io/ImageReader.java
@@ -262,7 +262,6 @@ public class ImageReader {
 		int base = 0;
 		float last = 0;
 		for (int k=0; k<fi.stripOffsets.length; k++) {
-			//IJ.log("seek: "+fi.stripOffsets[k]+" "+(in instanceof RandomAccessStream));
 			if (in instanceof RandomAccessStream)
 				((RandomAccessStream)in).seek(fi.stripOffsets[k]);
 			else if (k > 0) {
@@ -877,7 +876,7 @@ public class ImageReader {
 		return readPixels(is);
 	}
 	
-	byte[] uncompress(byte[] input) {
+	private byte[] uncompress(byte[] input) {
 		if (fi.compression==FileInfo.PACK_BITS)
 			return packBitsUncompress(input, fi.rowsPerStrip*fi.width*fi.getBytesPerPixel());
 		else if (fi.compression==FileInfo.LZW || fi.compression==FileInfo.LZW_WITH_DIFFERENCING)
diff --git a/ij/io/Opener.java b/ij/io/Opener.java
index 758b7a8..9e575f8 100644
--- a/ij/io/Opener.java
+++ b/ij/io/Opener.java
@@ -14,9 +14,8 @@ import java.awt.image.*;
 import java.io.*;
 import java.net.URL;
 import java.net.*;
-import java.util.Hashtable;
+import java.util.*;
 import java.util.zip.*;
-import java.util.Locale;
 import javax.swing.*;
 import javax.swing.filechooser.*;
 import java.awt.event.KeyEvent;
@@ -92,7 +91,7 @@ public class Opener {
 				(new PluginInstaller()).install(path);
 				return;
 		}
-		boolean fullPath = path.startsWith("/") || path.startsWith("\\") || path.indexOf(":\\")==1 || isURL;
+		boolean fullPath = path.startsWith("/") || path.startsWith("\\") || path.indexOf(":\\")==1 || path.indexOf(":/")==1 || isURL;
 		if (!fullPath) {
 			String defaultDir = OpenDialog.getDefaultDirectory();
 			if (defaultDir!=null)
@@ -1239,7 +1238,7 @@ public class Opener {
 			return OJJ;
 
 		// Results table (tab-delimited or comma-separated tabular text)
-		if (name.endsWith(".xls") || name.endsWith(".csv")) 
+		if (name.endsWith(".xls") || name.endsWith(".csv") || name.endsWith(".tsv")) 
 			return TABLE;
 
 		// AVI
@@ -1319,5 +1318,5 @@ public class Opener {
 	public static boolean getOpenUsingPlugins() {
 		return openUsingPlugins;
 	}
-	
+		
 }
diff --git a/ij/io/RoiDecoder.java b/ij/io/RoiDecoder.java
index 8566bdd..ccccc63 100644
--- a/ij/io/RoiDecoder.java
+++ b/ij/io/RoiDecoder.java
@@ -59,7 +59,7 @@ public class RoiDecoder {
 	public static final int SUBTYPE = 48;
 	public static final int OPTIONS = 50;
 	public static final int ARROW_STYLE = 52;
-	public static final int ELLIPSE_ASPECT_RATIO = 52;
+	public static final int FLOAT_PARAM = 52; //ellipse ratio or rotated rect width
 	public static final int POINT_TYPE= 52;
 	public static final int ARROW_HEAD_SIZE = 53;
 	public static final int ROUNDED_RECT_ARC_SIZE = 54;
@@ -87,6 +87,7 @@ public class RoiDecoder {
 	public static final int ARROW = 2;
 	public static final int ELLIPSE = 3;
 	public static final int IMAGE = 4;
+	public static final int ROTATED_RECT = 5;
 	
 	// options
 	public static final int SPLINE_FIT = 1;
@@ -296,13 +297,16 @@ public class RoiDecoder {
 						roiType = Roi.POLYGON;
 					else if (type==freehand) {
 						roiType = Roi.FREEROI;
-						if (subtype==ELLIPSE) {
+						if (subtype==ELLIPSE || subtype==ROTATED_RECT) {
 							double ex1 = getFloat(X1);		
 							double ey1 = getFloat(Y1);		
 							double ex2 = getFloat(X2);		
 							double ey2 = getFloat(Y2);
-							double aspectRatio = getFloat(ELLIPSE_ASPECT_RATIO);
-							roi = new EllipseRoi(ex1,ey1,ex2,ey2,aspectRatio);
+							double param = getFloat(FLOAT_PARAM);
+							if (subtype==ROTATED_RECT)
+								roi = new RotatedRectRoi(ex1,ey1,ex2,ey2,param);
+							else
+								roi = new EllipseRoi(ex1,ey1,ex2,ey2,param);
 							break;
 						}
 					} else if (type==traced)
diff --git a/ij/io/RoiEncoder.java b/ij/io/RoiEncoder.java
index 97ea647..928bd65 100644
--- a/ij/io/RoiEncoder.java
+++ b/ij/io/RoiEncoder.java
@@ -204,14 +204,20 @@ public class RoiEncoder {
 			putShort(RoiDecoder.STROKE_WIDTH, point.getSize());
 		}
 
-		if (roi instanceof EllipseRoi) {
-			putShort(RoiDecoder.SUBTYPE, RoiDecoder.ELLIPSE);
-			double[] p = ((EllipseRoi)roi).getParams();
+		if (roi instanceof RotatedRectRoi || roi instanceof EllipseRoi) {
+			double[] p = null;
+			if (roi instanceof RotatedRectRoi) {
+				putShort(RoiDecoder.SUBTYPE, RoiDecoder.ROTATED_RECT);
+				p = ((RotatedRectRoi)roi).getParams();
+			} else {
+				putShort(RoiDecoder.SUBTYPE, RoiDecoder.ELLIPSE);
+				p = ((EllipseRoi)roi).getParams();
+			}
 			putFloat(RoiDecoder.X1, (float)p[0]);
 			putFloat(RoiDecoder.Y1, (float)p[1]);
 			putFloat(RoiDecoder.X2, (float)p[2]);
 			putFloat(RoiDecoder.Y2, (float)p[3]);
-			putFloat(RoiDecoder.ELLIPSE_ASPECT_RATIO, (float)p[4]);
+			putFloat(RoiDecoder.FLOAT_PARAM, (float)p[4]);
 		}
 				
 		// save stroke width, stroke color and fill color (1.43i or later)
@@ -364,7 +370,7 @@ public class RoiEncoder {
 		//ij.IJ.log("putHeader2: "+hdr2Offset+" "+roiNameSize+"  "+roiName);
 		putInt(RoiDecoder.HEADER2_OFFSET, hdr2Offset);
 		putInt(hdr2Offset+RoiDecoder.C_POSITION, roi.getCPosition());
-		putInt(hdr2Offset+RoiDecoder.Z_POSITION, roi.getZPosition());
+		putInt(hdr2Offset+RoiDecoder.Z_POSITION, roi.hasHyperStackPosition()?roi.getZPosition():0);
 		putInt(hdr2Offset+RoiDecoder.T_POSITION, roi.getTPosition());
 		Overlay proto = roi.getPrototypeOverlay();
 		Color overlayLabelColor = proto.getLabelColor();
diff --git a/ij/io/TiffDecoder.java b/ij/io/TiffDecoder.java
index 9258a27..7420c03 100644
--- a/ij/io/TiffDecoder.java
+++ b/ij/io/TiffDecoder.java
@@ -194,12 +194,13 @@ public class TiffDecoder {
 		decode an IFD for each image. */
 	public void saveImageDescription(byte[] description, FileInfo fi) {
         String id = new String(description);
-        if (!id.startsWith("ImageJ"))
+        boolean createdByImageJ = id.startsWith("ImageJ");
+        if (!createdByImageJ)
 			saveMetadata(getName(IMAGE_DESCRIPTION), id);
 		if (id.length()<7) return;
 		fi.description = id;
         int index1 = id.indexOf("images=");
-        if (index1>0) {
+        if (index1>0 && createdByImageJ) {
             int index2 = id.indexOf("\n", index1);
             if (index2>0) {
                 String images = id.substring(index1+7,index2);
diff --git a/ij/macro/Functions.java b/ij/macro/Functions.java
index ca35e1f..aa7329f 100644
--- a/ij/macro/Functions.java
+++ b/ij/macro/Functions.java
@@ -1068,18 +1068,22 @@ public class Functions implements MacroConstants, Measurements {
 	Random ran;	
 	double random() {
 		double dseed = Double.NaN;
+		boolean gaussian = false;
 		if (interp.nextToken()=='(') {
 			interp.getLeftParen();
 			if (isStringArg()) {
 				String arg = getString().toLowerCase(Locale.US);
-				if (arg.indexOf("seed")==-1)
-					interp.error("'seed' expected");
-				interp.getComma();
-				dseed = interp.getExpression();
-				long seed = (long)dseed;
-				if (seed!=dseed)
-					interp.error("Seed not integer");
-				ran = new Random(seed);
+				if (arg.equals("seed")) {
+					interp.getComma();
+					dseed = interp.getExpression();
+					long seed = (long)dseed;
+					if (seed!=dseed)
+						interp.error("Seed not integer");
+					ran = new Random(seed);
+				} else if (arg.equals("gaussian"))
+					gaussian = true;
+				else
+					interp.error("'seed' or ''gaussian' expected");
 			}
 			interp.getRightParen();
 			if (!Double.isNaN(dseed)) return Double.NaN;
@@ -1087,7 +1091,10 @@ public class Functions implements MacroConstants, Measurements {
 		interp.getParens();
  		if (ran==null)
 			ran = new Random();
-		return ran.nextDouble();
+		if (gaussian)
+			return ran.nextGaussian();
+		else
+			return ran.nextDouble();
 	}
 	
 	//void setSeed() {
@@ -1244,11 +1251,19 @@ public class Functions implements MacroConstants, Measurements {
 	}
 
 	double getBoolean() {
-		String prompt = getStringArg();
+		interp.getLeftParen();
+		String prompt = getString();
+		String yesButton = "  Yes  ";
+		String noButton = "  No  ";
+		if (interp.nextToken()==',') {
+			yesButton = getNextString();
+			noButton = getNextString();
+		}
+		interp.getRightParen();
 		String title = interp.macroName!=null?interp.macroName:"";
 		if (title.endsWith(" Options"))
 			title = title.substring(0, title.length()-8);
-		YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(), title, prompt);
+		YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(), title, prompt, yesButton, noButton);
 		if (d.cancelPressed()) {
 			interp.done = true;
 			return 0.0;
@@ -2292,7 +2307,6 @@ public class Functions implements MacroConstants, Measurements {
 		float size = (float)getFirstArg();
 		int style = -1;
 		if (interp.nextToken()!=')') {
-			interp.getComma();
 			String options = getNextString().toLowerCase();
 			style = 0;
 			if (options.indexOf("bold") >= 0)
@@ -3244,8 +3258,11 @@ public class Functions implements MacroConstants, Measurements {
 			TextWindow tw = (TextWindow)frame;
 			if (isCommand)
 				handleTextWindowCommand(tw, s2);
-			else
+			else {
 				tw.append(s2);
+				TextPanel tp = tw.getTextPanel();
+				if (tp!=null) tp.setResultsTable(null);
+			}
 		}
 	}
 	
@@ -3712,9 +3729,13 @@ public class Functions implements MacroConstants, Measurements {
 					metadata = (String)imp.getProperty("Info");
 			} else 
 				metadata = imp.getStack().getSliceLabel(imp.getCurrentSlice());
-		} else
+		} else {
 			metadata = (String)imp.getProperty("Info");
-		if (metadata==null) metadata = "";
+			if (metadata==null && imp.getStackSize()>1)
+				metadata = imp.getStack().getSliceLabel(imp.getCurrentSlice());
+		}
+		if (metadata==null)
+			metadata = "";
 		return metadata;
 	}
 
@@ -4180,6 +4201,8 @@ public class Functions implements MacroConstants, Measurements {
 			Analyzer.setMeasurement(AREA, state);
 		else if (arg1.equals("mean"))
 			Analyzer.setMeasurement(MEAN, state);
+		else if (arg1.startsWith("perim"))
+			Analyzer.setMeasurement(PERIMETER, state);
 		else if (arg1.equals("stack position"))
 			Analyzer.setMeasurement(STACK_POSITION, state);
 		else if (arg1.startsWith("std"))
@@ -4202,6 +4225,10 @@ public class Functions implements MacroConstants, Measurements {
 			Prefs.autoContrast = state;
 		else if (arg1.equals("antialiasedtext"))
 			TextRoi.setAntialiasedText(state);
+		else if (arg1.equals("savebatchoutput"))
+			BatchProcessor.saveOutput(state);
+		else if (arg1.startsWith("converttomicrons"))
+			Prefs.convertToMicrons = state;
 		else
 			interp.error("Invalid option");
 	}
@@ -5244,6 +5271,10 @@ public class Functions implements MacroConstants, Measurements {
 			return showArray();
 		else if (name.equals("fourier"))
 			return fourierArray();
+		else if (name.equals("getVertexAngles"))
+			return getVertexAngles();
+		else if (name.equals("rotate"))
+			return rotateArray();
 		else
 			interp.error("Unrecognized Array function");
 		return null;
@@ -5561,16 +5592,33 @@ public class Functions implements MacroConstants, Measurements {
 		return a;
 	}
 	
+	Variable[] rotateArray() {
+		interp.getLeftParen();
+		Variable[] a = getArray();
+		interp.getComma();
+		int rot = (int) interp.getExpression();
+		interp.getRightParen();
+		int len = a.length;
+		while(rot<0)
+			rot += len;
+		Variable[] b = new Variable[len];
+		for (int i=0; i<len; i++) {
+			int dest = (i + rot)%len;
+			b[dest] = a[i];
+		}
+		for (int i=0; i<len; i++)
+			a[i]= b[i];
+		return a;
+	}
+
 	Variable[] findArrayMaxima(boolean minima) {
-		boolean excludeOnEdges = false;
+		int edgeMode = 0;
 		interp.getLeftParen();
 		Variable[] a = getArray();
 		double tolerance = getNextArg();
 		if (interp.nextToken()==',') {
 			interp.getComma();
-			double arg = interp.getBooleanExpression();
-			interp.checkBoolean(arg);
-			excludeOnEdges = arg==0?false:true;
+			edgeMode = (int)interp.getExpression();
 		}
 		interp.getRightParen();
 		int n = a.length;
@@ -5579,15 +5627,49 @@ public class Functions implements MacroConstants, Measurements {
 			d[i] = a[i].getValue();
 		int[] maxima = null;
 		if (minima)
-			maxima = MaximumFinder.findMinima(d, tolerance, excludeOnEdges);
+			maxima = MaximumFinder.findMinima(d, tolerance, edgeMode);
 		else
-			maxima = MaximumFinder.findMaxima(d, tolerance, excludeOnEdges);
+			maxima = MaximumFinder.findMaxima(d, tolerance, edgeMode);
 		int n2 = maxima.length;
 		Variable[] a2 = new Variable[n2];
 		for (int i=0; i<n2; i++)
 			a2[i] = new Variable(maxima[i]);
 		return a2;
 	}
+	
+	Variable[] getVertexAngles() {
+		interp.getLeftParen();
+		Variable[] xx = getArray();
+		interp.getComma();
+		Variable[] yy = getArray();
+		interp.getComma();
+		int arm = (int) interp.getExpression();
+		int len = xx.length;
+		if (yy.length != len)
+			interp.error("Same size expected");
+		double[] x = new double[len];
+		double[] y = new double[len];
+		double[] vAngles = new double[len];
+		interp.getRightParen();
+		Variable[] a2 = new Variable[len];
+		for (int jj = 0; jj < len; jj++) {
+			x[jj] = xx[jj].getValue();
+			y[jj] = yy[jj].getValue();
+		}
+		for (int mid = 0; mid < len; mid++) {
+			int left = (mid + 10 * len - arm) % len;
+			int right = (mid + arm) % len;
+			double dotprod = (x[right] - x[mid]) * (x[left] - x[mid]) + (y[right] - y[mid]) * (y[left] - y[mid]);
+			double crossprod = (x[right] - x[mid]) * (y[left] - y[mid]) - (y[right] - y[mid]) * (x[left] - x[mid]);
+			double phi = 180.0 - 180.0 / Math.PI * Math.atan2(crossprod, dotprod);
+			while (phi >= 180.0)
+				phi -= 360.0;
+			vAngles[mid] = phi;
+		}
+		for (int i = 0; i < len; i++)
+			a2[i] = new Variable(vAngles[i]);
+		return a2;
+	}
 
 	Variable[] showArray() {
 		int maxLength = 0;
@@ -5804,16 +5886,14 @@ public class Functions implements MacroConstants, Measurements {
 			if (roi==null)
 				return Double.NaN;;
 			if (imp.getStackSize()>1) {
-				if (imp.isHyperStack()) {
+				if (imp.isHyperStack() && roi.hasHyperStackPosition()) {
 					int c = roi.getCPosition();
 					int z = roi.getZPosition();
 					int t = roi.getTPosition();
-					if (c>0 || z>0 || t>0) {
-						c = c>0?c:imp.getChannel();
-						z = z>0?z:imp.getSlice();
-						t = t>0?t:imp.getFrame();
-						imp.setPosition(c, z, t);
-					}
+					c = c>0?c:imp.getChannel();
+					z = z>0?z:imp.getSlice();
+					t = t>0?t:imp.getFrame();
+					imp.setPosition(c, z, t);
 				} else if (roi.getPosition()>0)
 					imp.setSlice(roi.getPosition());
 			}
@@ -5828,6 +5908,9 @@ public class Functions implements MacroConstants, Measurements {
 			roi.setLocation(x, y);
 			imp.draw();
 			return Double.NaN;
+		} else if (name.equals("measure")) {
+			ResultsTable rt = overlay.measure(imp);
+			rt.show("Results");
 		} else
 			interp.error("Unrecognized function name");
 		return Double.NaN;
@@ -6204,7 +6287,7 @@ public class Functions implements MacroConstants, Measurements {
 			String properties = roi.getProperties();
 			return properties!=null?properties:"";
 		} else if (name.equals("setFillColor")) {
-			roi.setFillColor(Colors.decode(getStringArg(),null));
+			roi.setFillColor(getRoiColor());
 			imp.draw();
 			return null;
 		} else if (name.equals("move")) {
@@ -6214,7 +6297,7 @@ public class Functions implements MacroConstants, Measurements {
 			roi.setName(getStringArg());
 			return null;
 		} else if (name.equals("setStrokeColor")) {
-			roi.setStrokeColor(Colors.decode(getStringArg(),null));
+			roi.setStrokeColor(getRoiColor());
 			imp.draw();
 			return null;
 		} else if (name.equals("setStrokeWidth")) {
@@ -6225,6 +6308,8 @@ public class Functions implements MacroConstants, Measurements {
 			String value = "1";
 			interp.getLeftParen();
 			String key = getString();
+			if (key.contains(" "))
+				interp.error("Keys contain a space");
 			if (interp.nextToken()==',') {
 				interp.getComma();
 				value = getString();
@@ -6245,6 +6330,52 @@ public class Functions implements MacroConstants, Measurements {
 		return null;
 	}
 	
+	/*
+	private String getRoiPosition(Roi roi) {
+		Variable channel = getFirstVariable();
+		Variable slice = getNextVariable();
+		Variable frame = getLastVariable();
+		int c = roi.getCPosition();
+		int z = roi.getZPosition();
+		int t = roi.getTPosition();
+		channel.setValue(c);
+		slice.setValue(z);
+		frame.setValue(t);
+		return null;
+	}
+
+	private String setRoiPosition(ImagePlus imp, Roi roi) {
+		int channel = (int)getFirstArg();
+		int slice = (int)getNextArg();
+		int frame = (int)getLastArg();
+		if (channel<=1 && frame<=1 && !imp.isHyperStack())
+			roi.setPosition(slice);
+		else
+			roi.setPosition(channel, slice, frame);
+		return null;
+	}
+	*/
+
+	private Color getRoiColor() {
+		interp.getLeftParen();
+		if (isStringArg()) {
+			Color color = Colors.decode(getString(),null);
+			interp.getRightParen();
+			return color;
+		} else {
+			int r = (int)interp.getExpression();
+			if (interp.nextToken()==')') {
+				interp.getRightParen();
+				return new Color(r);
+			}
+			int g = (int)getNextArg();
+			int b = (int)getLastArg();
+			if (r<0) r=0; if (g<0) g=0; if (b<0) b=0;
+			if (r>255) r=255; if (g>255) g=255; if (b>255) b=255;
+			return new Color(r, g, b);
+		}
+	}
+
 	private void getContainedPoints(Roi roi) {
 		Variable xCoordinates = getFirstArrayVariable();
 		Variable yCoordinates = getLastArrayVariable();
diff --git a/ij/macro/Interpreter.java b/ij/macro/Interpreter.java
index d67eac8..d30708d 100644
--- a/ij/macro/Interpreter.java
+++ b/ij/macro/Interpreter.java
@@ -67,6 +67,7 @@ public class Interpreter implements MacroConstants {
 	static TextWindow arrayWindow;
 	int inspectStkIndex = -1;
 	int inspectSymIndex = -1;
+	boolean evaluating;
 
 
 	/** Interprets the specified string. */
@@ -111,11 +112,23 @@ public class Interpreter implements MacroConstants {
 		if (func==null)
 			func = new Functions(this, pgm);
 		func.plot = null;
-		//IJ.showStatus("interpreting");
 		doStatements();
 		finishUp();
 	}
 
+	/** Evaluates macro code. */
+	/*
+	public void eval(String macro) {
+		Tokenizer tok = new Tokenizer();
+		this.pgm = tok.tokenize(macro);
+		evaluating = true;
+		pushGlobals();
+		if (func==null)
+			func = new Functions(this, pgm);
+		run(0);
+	}
+	*/
+
 	/** Runs an existing macro starting at the specified program counter location. */
 	public void run(int location) {
 		topOfStack = topOfGlobals;
@@ -1178,7 +1191,8 @@ public class Interpreter implements MacroConstants {
 		imageTable = null;
 		WindowManager.setTempCurrentImage(null);
 		wasError = true;
-		instance = null;
+		if (!evaluating)
+			instance = null;
 		if (showMessage && message!=null) {
 			String line = getErrorLine();
 			done = true;
diff --git a/ij/measure/ResultsTable.java b/ij/measure/ResultsTable.java
index 98877ed..8554270 100644
--- a/ij/measure/ResultsTable.java
+++ b/ij/measure/ResultsTable.java
@@ -437,9 +437,8 @@ public class ResultsTable implements Cloneable {
 		if (column==null)
 			throw new IllegalArgumentException("Column is null");
 		int col = getColumnIndex(column);
-		if (col==COLUMN_NOT_FOUND) {
+		if (col==COLUMN_NOT_FOUND)
 			col = getFreeColumn(column);
-		}
 		setValue(col, row, value);
 	}
 
@@ -829,7 +828,7 @@ public class ResultsTable implements Cloneable {
 			TextWindow win;
 			if (frame!=null && frame instanceof TextWindow) {
 				win = (TextWindow)frame;
-				if (windowTitle==null || !windowTitle.startsWith("Counts_"))
+				if (!IJ.isMacro() && (windowTitle==null || !windowTitle.startsWith("Counts_")))
 					win.toFront();
 			} else {
 				int width = getLastColumn()<=0?250:400;
@@ -1129,5 +1128,7 @@ public class ResultsTable implements Cloneable {
 	public String toString() {
 		return ("ctr="+counter+", hdr="+getColumnHeadings());
 	}
+	
+
 		
 }
diff --git a/ij/plugin/AVI_Reader.java b/ij/plugin/AVI_Reader.java
index de13bf9..1e31e53 100644
--- a/ij/plugin/AVI_Reader.java
+++ b/ij/plugin/AVI_Reader.java
@@ -92,9 +92,13 @@ import javax.imageio.ImageIO;
  *		- 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)
+ *	 2017-04-21
+ *		- bugfix: file was not closed in case of dialog cancelled or some IO errors.
+ *      - Tries to recover data from truncated files.
+ *
  *
  * The AVI format looks like this:
- * RIFF AVI					RIFF HEADER, AVI CHUNK					
+ * RIFF AVI					RIFF HEADER, AVI CHUNK
  *	 | LIST hdrl			MAIN AVI HEADER
  *	 | | avih				AVI HEADER
  *	 | | LIST strl			STREAM LIST(s) (One per stream)
@@ -171,7 +175,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	private final static int   YV12_COMPRESSION	 = 0x32315659; //'YV12' - y plane followed by 2x2 subsampled V and U
 	private final static int   NV12_COMPRESSION	 = 0x3231564E; //'NV12' - y plane followed by 2x2 subsampled interleaved U, V
 	private final static int   NV21_COMPRESSION	 = 0x3132564E; //'NV21' - y plane followed by 2x2 subsampled interleaved V, U
-	
+
 	private final static int   JPEG_COMPRESSION	 = 0x6765706a; //'jpeg' JPEG compression of individual frames
 	private final static int   JPEG_COMPRESSION2 = 0x4745504a; //'JPEG' JPEG compression of individual frames
 	private final static int   JPEG_COMPRESSION3 = 0x04;	   //BI_JPEG: JPEG compression of individual frames
@@ -184,7 +188,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	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 
+	// flags from AVI chunk header
 	private final static int   AVIF_HASINDEX	 = 0x00000010;	// Index at end of file?
 	private final static int   AVIF_MUSTUSEINDEX = 0x00000020;	// ignored by this plugin
 	private final static int   AVIF_ISINTERLEAVED= 0x00000100;	// ignored by this plugin
@@ -230,7 +234,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	private	 ColorModel		   cm;
 	private	 boolean		   variableLength;		//compressed (PNG, JPEG) frames have variable length
 	//for conversion to ImageJ stack
-	private	 Vector<long[]>	   frameInfos;	//for virtual stack: long[] with frame pos&size in file, time(usec)
+	private	 Vector<long[]>	   frameInfos;			//for virtual stack: long[] with frame pos&size in file, time(usec)
 	private	 ImageStack		   stack;
 	private	 ImagePlus		   imp;
 	//for debug messages and error handling
@@ -238,6 +242,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	private	 long			   startTime;
 	private	 boolean		   aborting;
 	private	 boolean		   displayDialog = true;
+    private  String 		   errorText;			//error occurred during makeStack, or null
 
 	//From AVI Header Chunk
 	private	 int			   dwMicroSecPerFrame;
@@ -299,25 +304,28 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		} catch (Exception e) {
 			error(exceptionMessage(e));
 			return;
+		} finally {
+			closeFile(raFile);
 		}
-		if (displayDialog && !showDialog(fileName))
-			return;	 //ask for parameters
-		try {
-			ImageStack stack = makeStack(path, firstFrame, lastFrame, isVirtual, convertToGray, flipVertical);	//read data
-		} catch (Exception e) {
-			error(exceptionMessage(e));
-			return;
-		}
-		if (stack==null || aborting || (stack.isVirtual()&&stack.getProcessor(1)==null))
+		if (displayDialog && !showDialog(fileName))					//ask for parameters
 			return;
-		if (stack.getSize() == 0) {
-			String rangeText = "";
-			if (firstFrame>1 || lastFrame!=0)
-				rangeText = "\nin Range "+firstFrame+
-					(lastFrame>0 ? " - "+lastFrame : " - end");
-			error("Error: No Frames Found"+rangeText);
+		errorText = null;
+		ImageStack stack = makeStack(path, firstFrame, lastFrame, isVirtual, convertToGray, flipVertical);	//read data
+		if (aborting)
+			return;													//error message has been shown already
+		if (stack==null || stack.getSize() == 0 || stack.getProcessor(1)==null) {	//read nothing?
+            if (errorText != null)
+				error(errorText);
+            else {
+				 String rangeText = "";
+				if (firstFrame > 1 || (lastFrame != 0 && lastFrame != dwTotalFrames))
+					rangeText = "\nin Range "+firstFrame+
+                            (lastFrame>0 ? " - "+lastFrame : " - end");
+                error("Error: No Frames Found"+rangeText);
+            }
 			return;
-		}
+		} else if (errorText != null)
+            IJ.showMessage("AVI Reader", errorText);				//show the error, e.g. we may have an incomplete stack
 		imp = new ImagePlus(WindowManager.makeUniqueName(fileName), stack);
 		if (imp.getBitDepth()==16)
 			imp.getProcessor().resetMinAndMax();
@@ -335,7 +343,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	public ImagePlus getImagePlus() {
 		return imp;
 	}
-	
+
 	/** Opens an AVI file as a virtual stack. The ImagePlus is not displayed. */
 	public static ImagePlus openVirtual(String path) {
 		return open(path, true);
@@ -357,8 +365,10 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 	 * @param lastFrame	  Number of last frame to read or 0 for reading all, -1 for all but last...
 	 * @param isVirtual			Whether to return a virtual stack
 	 * @param convertToGray		Whether to convert color images to grayscale
-	 * @return	Returns the stack; null on failure.
-	 *	The stack returned may be non-null, but have a length of zero if no suitable frames were found
+	 * @return	Returns the stack (may be incomplete on error); null on failure.
+	 *	The stack returned may be non-null, but have a length of zero if no suitable frames were found.
+	 *  Use <code>getErrorText</code> to determine whether there has been an error reading the file.
+	 *  For virtual stacks, not that I/O errors may also occur later, when reading the frames.
 	 */
 	public ImageStack makeStack (String path, int firstFrame, int lastFrame,
 			boolean isVirtual, boolean convertToGray, boolean flipVertical) {
@@ -367,37 +377,37 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		this.isVirtual = isVirtual;
 		this.convertToGray = convertToGray;
 		this.flipVertical = flipVertical;
-		String exceptionMessage = null;
 		IJ.showProgress(.001);
 		try {
 			readAVI(path);
 		} catch (OutOfMemoryError e) {
 			stack.trim();
-			IJ.showMessage("AVI Reader", "Out of memory.  " + stack.getSize() + " of " + dwTotalFrames + " frames will be opened.");
+			errorText = "Out of memory.  " + stack.getSize() + " of " + dwTotalFrames + " frames will be opened.";
 		} catch (Exception e) {
-			exceptionMessage = exceptionMessage(e);
+			errorText = exceptionMessage(e);
+            if (isVirtual || stack==null || stack.getSize()==0)		//return null only if we have really nothing
+				return null;
 		} finally {
-			try {
-				raFile.close();
-				if (verbose)
-					IJ.log("File closed.");
-			} catch (Exception e) {}
+			closeFile(raFile);
+			if (verbose)
+                IJ.log("File closed.");
 			IJ.showProgress(1.0);
 		}
-		if (exceptionMessage != null) {
-		   error(exceptionMessage);
-		   return null;
-		}
 		if (isVirtual && frameInfos != null)
 			stack = this;
 		if (stack!=null && cm!=null)
 			stack.setColorModel(cm);
 		return stack;
-	} 
+	}
+
+    /** Returns a description of the error reading the file with <code>makeStack</code> or null if no error */
+	public String getErrorText() {
+		return errorText;
+	}
 
 	/** Returns an ImageProcessor for the specified slice of this virtual stack (if it is one)
-		where 1<=n<=nslices. Returns null if no virtual stack or no slices.
-	*/
+     *  where 1<=n<=nslices. Returns null if no virtual stack or no slices or error reading the frame.
+	 */
 	public synchronized ImageProcessor getProcessor(int n) {
 		if (frameInfos==null || frameInfos.size()==0 || raFilePath==null)
 			return null;
@@ -405,21 +415,15 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 			throw new IllegalArgumentException("Argument out of range: "+n);
 		Object pixels = null;
 		RandomAccessFile rFile = null;
-		String exceptionMessage = null;
 		try {
 			rFile = new RandomAccessFile(new File(raFilePath), "r");
 			long[] frameInfo = (long[])(frameInfos.get(n-1));
 			pixels = readFrame(rFile, frameInfo[0], (int)frameInfo[1]);
 		} catch (Exception e) {
-			exceptionMessage = exceptionMessage(e);
-		} finally {
-			try {
-				rFile.close();
-			} catch (Exception e) {}
-		}
-		if (exceptionMessage != null) {
-			error(exceptionMessage);
+			error(exceptionMessage(e));
 			return null;
+		} finally {
+			closeFile(rFile);
 		}
 		if (pixels == null) return null; //failed
 		if (pixels instanceof byte[])
@@ -496,9 +500,12 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 
 	/** Read into a (virtual) stack */
 	private void readAVI(String path) throws Exception, IOException {
-
 		if (!headerOK)							// we have not read the header yet?
 			openAndReadHeader(path);
+        else {
+			File file = new File(path);			// open if currently not open
+			raFile = new RandomAccessFile(file, "r");
+        }
 		startTime += System.currentTimeMillis();// taking previously elapsed time into account
 		/** MOVED UP HERE BY JSP*/
 		if (lastFrame > 0)						// last frame number to read: from Dialog
@@ -617,9 +624,13 @@ 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>endPosition || nextPos>fileSize) {
+				errorText = "AVI File Error: '"+fourccString(type)+"' @ 0x"+Long.toHexString(raFile.getFilePointer()-8)+" has invalid length. File damaged/truncated?";
+				IJ.log(errorText);		// this text is also remembered as error message for showing in message box
+				if (fourcc == FOURCC_movi)
+                    nextPos = fileSize;	// if movie data truncated, try reading what we can get
+				else
+                    return -1;			// otherwise, nothing to be done
 			}
 			if (isList && type == FOURCC_LIST)
 				type = readInt();
@@ -1011,7 +1022,7 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		if (isPlanarFormat && ((biWidth&1)!=0 || (biHeight&1)!=0))
 			throw new Exception("Odd size ("+biWidth+"x"+biHeight+") unsupported with "+fourccString(biCompression)+" compression");
 		// raw & interleaved YUV: scan line is padded with zeroes to be a multiple of four bytes
-		scanLineSize = isPlanarFormat ? 
+		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 there is a palette
@@ -1397,7 +1408,14 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 		}
 		return s;
 	}
-	
+
+	/** tries to close the given file (if not null) */
+	private void closeFile(RandomAccessFile rFile) {
+		if (rFile != null) try {
+			rFile.close();
+        } catch (Exception e) {}
+	}
+
 	private void error(String msg) {
 		 aborting = true;
 		 IJ.error("AVI Reader", msg);
@@ -1548,9 +1566,9 @@ public class AVI_Reader extends VirtualStack implements PlugIn {
 			readableSize += HUFFMAN_LENGTH;
 		}
 	}
-	
+
 	public void displayDialog(boolean displayDialog) {
 		this.displayDialog = displayDialog;
 	}
-	
+
 }
diff --git a/ij/plugin/BatchProcessor.java b/ij/plugin/BatchProcessor.java
index 73daf58..7dd008f 100644
--- a/ij/plugin/BatchProcessor.java
+++ b/ij/plugin/BatchProcessor.java
@@ -15,6 +15,7 @@ import java.util.Vector;
 		private static final String MACRO_FILE_NAME = "BatchMacro.ijm";
 		private static final String[] formats = {"TIFF", "8-bit TIFF", "JPEG", "GIF", "PNG", "PGM", "BMP", "FITS", "Text Image", "ZIP", "Raw"};
 		private static String format = Prefs.get("batch.format", formats[0]);
+		
 		private static final String[] code = {
 			"[Select from list]",
 			"Add Border",
@@ -32,6 +33,22 @@ import java.util.Vector;
 			"Show File Info",
 			"Unsharp Mask",
 		};
+		
+		private static final String help = "<html>"
+		+"<h1>Process>Batch>Virtual Stack</h1>"
+		+"<font size=+1>"
+		+"This command runs macro code on each image in a virtual stack.<br>"
+		+"The processed images are saved in the <i>Output</i> folder,<br>"
+		+"in the specified <i>Format</i>, allowing them to be opened as a<br>"
+		+"virtual stack. Make sure the <i>Output</i> folder is empty<br>"
+		+"before clicking on <i>Process</i>.<br>"
+		+"<br>"
+		+"In the macro code, the 'i' (index) and 'n' (stack size) variables<br>"
+		+"are predefined. Call <i>setOption('SaveBatchOutput',false)</i> to<br>"
+		+"prevent the image currently being processed from being saved,<br>"
+		+"effectively removing it from the output virtual stack.<br> <br>"
+		+"</font>";
+
 		private String macro = "";
 		private int testImage;
 		private Button input, output, open, save, test;
@@ -42,6 +59,7 @@ import java.util.Vector;
 		private ImagePlus outputImage;
 		private boolean errorDisplayed;
 		private String filter;
+		private static boolean saveOutput = true;
 
 	public void run(String arg) {
 		if (arg.equals("stack")) {
@@ -105,9 +123,9 @@ import java.util.Vector;
 		gd = new NonBlockingGenericDialog("Batch Process");
 		addPanels(gd);
 		gd.setInsets(15, 0, 5);
-		gd.addChoice("Output Format:", formats, format);
+		gd.addChoice("Output format:", formats, format);
 		gd.setInsets(0, 0, 5);
-		gd.addChoice("Add Macro Code:", code, code[0]);
+		gd.addChoice("Add macro code:", code, code[0]);
 		if (virtualStack==null)
 			gd.addStringField("File name contains:", "", 10);
 		gd.setInsets(15, 10, 0);
@@ -117,6 +135,8 @@ import java.util.Vector;
 		gd.setOKLabel("Process");
 		Vector choices = gd.getChoices();
 		Choice choice = (Choice)choices.elementAt(1);
+		if (virtualStack!=null)
+			gd.addHelp(help);
 		choice.addItemListener(this);
 		gd.showDialog();
 		format = gd.getNextChoice();
@@ -135,12 +155,12 @@ import java.util.Vector;
 			IJ.showProgress(i, n);
 			ImageProcessor ip = stack.getProcessor(i);
 			if (ip==null) return;
-			ImagePlus imp = new ImagePlus("", ip);
+			ImagePlus imp = new ImagePlus(i+"/"+stack.getSize(), ip);
 			if (!macro.equals("")) {
-				if (!runMacro("i="+(index++)+";"+macro, imp))
+				if (!runMacro("i="+(index++)+";"+"n="+stack.getSize()+";"+macro, imp))
 					break;
 			}
-			if (!outputPath.equals("")) {
+			if (saveOutput && !outputPath.equals("")) {
 				if (format.equals("8-bit TIFF") || format.equals("GIF")) {
 					if (imp.getBitDepth()==24)
 						IJ.run(imp, "8-bit Color", "number=256");
@@ -149,6 +169,7 @@ import java.util.Vector;
 				}
 				IJ.saveAs(imp, format, outputPath+pad(i));
 			}
+			saveOutput = true;
 			imp.close();
 		}
 		if (outputPath!=null && !outputPath.equals(""))
@@ -195,7 +216,7 @@ import java.util.Vector;
 				if (!runMacro("i="+(index++)+";"+macro, imp))
 					break;
 			}
-			if (!outputPath.equals("")) {
+			if (saveOutput && !outputPath.equals("")) {
 				if (format.equals("8-bit TIFF") || format.equals("GIF")) {
 					if (imp.getBitDepth()==24)
 						IJ.run(imp, "8-bit Color", "number=256");
@@ -207,6 +228,7 @@ import java.util.Vector;
 				else
 					IJ.saveAs(imp, format, outputPath+list[i]);
 			}
+			saveOutput = true;
 			imp.close();
 		}
 	}
@@ -454,6 +476,9 @@ import java.util.Vector;
 		OpenDialog.setLastDirectory(f.getParent()+File.separator);
 		OpenDialog.setLastName(f.getName());
 	}
-
+	
+	public static void saveOutput(boolean b) {
+		saveOutput = b;
+	}
 
 }
diff --git a/ij/plugin/Binner.java b/ij/plugin/Binner.java
index b8c3e7f..c7255e0 100644
--- a/ij/plugin/Binner.java
+++ b/ij/plugin/Binner.java
@@ -65,7 +65,7 @@ public class Binner implements PlugIn {
 		}
 		if (zshrink>1 && !imp.isHyperStack())
 			stack2 = shrinkZ(stack2, zshrink);
-		ImagePlus imp2 = (ImagePlus)imp.clone();
+		ImagePlus imp2 = imp.createImagePlus();
 		imp2.setStack("Reduced "+imp.getShortTitle(), stack2);
 		Calibration cal2 = imp2.getCalibration();
 		if (cal2.scaled()) {
diff --git a/ij/plugin/CalibrationBar.java b/ij/plugin/CalibrationBar.java
index 3c3b7a1..55737b7 100644
--- a/ij/plugin/CalibrationBar.java
+++ b/ij/plugin/CalibrationBar.java
@@ -56,7 +56,8 @@ public class CalibrationBar implements PlugIn {
 	int userPadding = 0;
 	int fontHeight = 0;
 	boolean boldText;
-	boolean flatten;
+	static boolean staticFlatten;
+	boolean flatten = staticFlatten;
 	Object backupPixels;
 	byte[] byteStorage;
 	int[] intStorage;
@@ -166,6 +167,8 @@ public class CalibrationBar implements PlugIn {
 		zoom = (double)gd.getNextNumber();
 		boldText = gd.getNextBoolean();
 		flatten = !gd.getNextBoolean();
+		if (!IJ.isMacro())
+			staticFlatten = flatten;
 		return true;
 	}
 
diff --git a/ij/plugin/ChannelArranger.java b/ij/plugin/ChannelArranger.java
index c70f3d0..3ffce6a 100644
--- a/ij/plugin/ChannelArranger.java
+++ b/ij/plugin/ChannelArranger.java
@@ -131,10 +131,8 @@ public class ChannelArranger implements PlugIn, TextListener {
 			}
 			img2.setOverlay(overlay);
 		}
-		if (img.getWindow()!=null) {
-			img.changes = false;
-			img.close();
-		}
+		img.changes = false;
+		img.close();
 		return img2;
 	}
 
diff --git a/ij/plugin/Colors.java b/ij/plugin/Colors.java
index 81a7d42..74ea655 100644
--- a/ij/plugin/Colors.java
+++ b/ij/plugin/Colors.java
@@ -229,5 +229,5 @@ public class Colors implements PlugIn, ItemListener {
 			names.add(arg);
 		return (String[])names.toArray(new String[names.size()]);
 	}
-
+	
 }
diff --git a/ij/plugin/Converter.java b/ij/plugin/Converter.java
index 2be8ebe..4a68a1f 100644
--- a/ij/plugin/Converter.java
+++ b/ij/plugin/Converter.java
@@ -15,8 +15,12 @@ public class Converter implements PlugIn {
 		imp = WindowManager.getCurrentImage();
 		if (imp!=null) {
 			if (imp.isComposite() && arg.equals("RGB Color") && !imp.getStack().isRGB() && !imp.getStack().isHSB() && !imp.getStack().isLab()) {
-				(new RGBStackConverter()).run("");
-				imp.setTitle(imp.getTitle()); // updates size in Window menu
+				if (imp.getWindow()==null && !ij.macro.Interpreter.isBatchMode())
+					RGBStackConverter.convertToRGB(imp);
+				else {
+					(new RGBStackConverter()).run("");
+					imp.setTitle(imp.getTitle()); // updates size in Window menu
+				}
 			} else if (imp.lock()) {
 				convert(arg);
 				imp.unlock();
diff --git a/ij/plugin/Duplicator.java b/ij/plugin/Duplicator.java
index 2fd327b..f4c4fcb 100644
--- a/ij/plugin/Duplicator.java
+++ b/ij/plugin/Duplicator.java
@@ -162,6 +162,9 @@ public class Duplicator implements PlugIn, TextListener, ItemListener {
 		if (roi!=null && roi.isArea())
 			rect = roi.getBounds();
 		ImageStack stack = imp.getStack();
+		boolean virtualStack = stack.isVirtual();
+		double min = imp.getDisplayRangeMin();
+		double max = imp.getDisplayRangeMax();
 		ImageStack stack2 = null;
 		for (int i=firstSlice; i<=lastSlice; i++) {
 			if (stack.isVirtual())
@@ -184,6 +187,8 @@ public class Duplicator implements PlugIn, TextListener, ItemListener {
 			imp2.setDimensions(1, 1, size);
 		else
 			imp2.setDimensions(1, size, 1);
+		if (virtualStack)
+			imp2.setDisplayRange(min, max);
 		Overlay overlay = imp.getOverlay();
 		if (overlay!=null && !imp.getHideOverlay()) {
 			Overlay overlay2 = overlay.crop(rect);
diff --git a/ij/plugin/FileInfoVirtualStack.java b/ij/plugin/FileInfoVirtualStack.java
index fa634c0..77244b4 100644
--- a/ij/plugin/FileInfoVirtualStack.java
+++ b/ij/plugin/FileInfoVirtualStack.java
@@ -234,5 +234,5 @@ public class FileInfoVirtualStack extends VirtualStack implements PlugIn {
 		}
 		info[nImages-1] = fileInfo;
 	}
-	
-	}
+		
+}
diff --git a/ij/plugin/FolderOpener.java b/ij/plugin/FolderOpener.java
index e466fa1..9c05b18 100644
--- a/ij/plugin/FolderOpener.java
+++ b/ij/plugin/FolderOpener.java
@@ -3,6 +3,7 @@ import java.awt.*;
 import java.io.*;
 import java.awt.event.*;
 import java.awt.image.ColorModel;
+import java.util.Properties;
 import ij.*;
 import ij.io.*;
 import ij.gui.*;
@@ -305,6 +306,11 @@ public class FolderOpener implements PlugIn {
 			fi.directory = directory;
 			imp2.setFileInfo(fi); // saves FileInfo of the first image
 			imp2.setOverlay(overlay);
+			if (stack instanceof VirtualStack) {
+				Properties props = ((VirtualStack)stack).getProperties();
+				if (props!=null)
+					imp2.setProperty("FHT", props.get("FHT"));
+			}
 			if (allSameCalibration) {
 				// use calibration from first image
 				if (scale!=100.0 && cal.scaled()) {
diff --git a/ij/plugin/Grid.java b/ij/plugin/Grid.java
index 3dc8bd8..348647e 100644
--- a/ij/plugin/Grid.java
+++ b/ij/plugin/Grid.java
@@ -32,6 +32,8 @@ public class Grid implements PlugIn, DialogListener {
 	private String color = "Cyan";
 	private boolean bold;
 	private boolean randomOffset;
+	private boolean centered;
+	private Checkbox centerCheckbox, randomCheckbox;
 
 	public void run(String arg) {
 		imp = IJ.getImage();
@@ -169,8 +171,14 @@ public class Grid implements PlugIn, DialogListener {
 		gd.addNumericField("Area per point:", areaPerPoint, places, 6, units+"^2");
 		gd.addChoice("Color:", colors, color);
 		gd.addCheckbox("Bold", bold);
+		gd.addCheckbox("Center grid on image", centered);
 		gd.addCheckbox("Random offset", randomOffset);
 		gd.addDialogListener(this);
+		if (!isMacro) {
+			Vector v = gd.getCheckboxes();
+			centerCheckbox = (Checkbox)v.elementAt(1);
+			randomCheckbox = (Checkbox)v.elementAt(2);
+		}
 		dialogItemChanged(gd, null);
 		gd.showDialog();
 		if (gd.wasCanceled()) {
@@ -193,7 +201,13 @@ public class Grid implements PlugIn, DialogListener {
 		areaPerPoint = gd.getNextNumber();
 		color = gd.getNextChoice();
 		bold = gd.getNextBoolean();
+		centered = gd.getNextBoolean();
 		randomOffset = gd.getNextBoolean();
+		if (randomOffset) {
+			centered = false;
+			if (centerCheckbox!=null)
+				centerCheckbox.setState(false);
+		}
 		double minArea= (width*height)/50000.0;
 		if (type.equals(types[CROSSES])&&minArea<50.0)
 			minArea = 50.0;
@@ -210,7 +224,10 @@ public class Grid implements PlugIn, DialogListener {
 		double tileSize = Math.sqrt(areaPerPoint);
 		tileWidth = tileSize/pixelWidth;
 		tileHeight = tileSize/pixelHeight;
-		if (randomOffset) {
+		if (centered) {
+			xstart = (int)Math.round((width%tileWidth)/2.0);
+			ystart = (int)Math.round((height%tileHeight)/2.0);
+		} else if (randomOffset) {
 			xstart = (int)(random.nextDouble()*tileWidth);
 			ystart = (int)(random.nextDouble()*tileHeight);
 		} else {
@@ -222,10 +239,11 @@ public class Grid implements PlugIn, DialogListener {
 		if (gd.invalidNumber())
 			return true;
 		drawGrid();
-        	return true;
+        return true;
 	}
 
 	private void drawGrid() {
+		//IJ.log(centered+" "+xstart+" "+ystart);
 		if (type.equals(types[LINES]))
 			drawLines();
 		else if (type.equals(types[HLINES]))
@@ -242,6 +260,7 @@ public class Grid implements PlugIn, DialogListener {
 	
 	private void getSettings() {
 		String prefs = Prefs.get(OPTIONS, "Lines,Cyan,-");
+		//IJ.log("options: "+prefs);
 		String[] options = Tools.split(prefs, ",");
 		if (options.length>=3) {
 			type = options[0];
@@ -250,13 +269,19 @@ public class Grid implements PlugIn, DialogListener {
 			areaPerPoint = saveAreaPerPoint;
 			color = options[1];
 			bold = options[2].contains("bold");
+			centered = options[2].contains("centered");
 			randomOffset = options[2].contains("random");
+			if (centered)
+				randomOffset = false;
 		}
 	}
 	
 	private void saveSettings() {
-		String options = type+","+color+","+(bold?"bold":"")+" "+(randomOffset?"random":"");
-		Prefs.set(OPTIONS, options);
+		String options = type+","+color+",";
+		String options2 = (bold?"bold ":"")+(centered?"centered ":"")+(randomOffset?"random ":"");
+		if (options2.length()==0)
+			options2 = "-";
+		Prefs.set(OPTIONS, options+options2);
 		saveAreaPerPoint = areaPerPoint;
 	}
 
diff --git a/ij/plugin/GroupedZProjector.java b/ij/plugin/GroupedZProjector.java
index 1991843..ea6f72c 100644
--- a/ij/plugin/GroupedZProjector.java
+++ b/ij/plugin/GroupedZProjector.java
@@ -35,14 +35,26 @@ public class GroupedZProjector implements PlugIn {
 		if (method<0 || method>=ZProjector.METHODS.length)
 			return null;
 		int[] dim = imp.getDimensions();
-		imp.setDimensions(1, groupSize, imp.getStackSize()/groupSize);
+		int projectedStackSize = imp.getStackSize()/groupSize;
+		imp.setDimensions(1, groupSize, projectedStackSize);
 		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();
+
+		ImagePlus zProjectorOutput = zp.getProjection();
+		int[] zProjectDim = zProjectorOutput.getDimensions();
+		for (int i=2; i<dim.length; i++) {
+			if (dim[i] != 1)
+				zProjectDim[i] = projectedStackSize;
+			else
+				zProjectDim[i] = 1;
+		}
+		// Fix dimensions for output ImagePlus
+		zProjectorOutput.setDimensions(zProjectDim[2], zProjectDim[3], zProjectDim[4]);
+		return zProjectorOutput;
 	}
 	
 	boolean showDialog(ImagePlus imp) {
diff --git a/ij/plugin/HyperStackConverter.java b/ij/plugin/HyperStackConverter.java
index 86b937d..6514af6 100644
--- a/ij/plugin/HyperStackConverter.java
+++ b/ij/plugin/HyperStackConverter.java
@@ -86,7 +86,7 @@ public class HyperStackConverter implements PlugIn {
 		return imp2;
 	}
 		
-	/** Displays the current stack in a HyperStack window. Based on the 
+	/** Displays the specified stack in a HyperStack window. Based on the 
 		Stack_to_Image5D class in Joachim Walter's Image5D plugin. */
 	void convertStackToHS(ImagePlus imp) {
         int nChannels = imp.getNChannels();
diff --git a/ij/plugin/ImageInfo.java b/ij/plugin/ImageInfo.java
index 106579a..ed612e2 100644
--- a/ij/plugin/ImageInfo.java
+++ b/ij/plugin/ImageInfo.java
@@ -274,6 +274,9 @@ public class ImageInfo implements PlugIn {
     	double mag = ic!=null?ic.getMagnification():1.0;
     	if (mag!=1.0)
 			s += "Magnification: " + IJ.d2s(mag,2) + "\n";
+		if (ic!=null)
+			s += "ScaleToFit: " + ic.getScaleToFit() + "\n";
+
 			
 	    if (cal.calibrated()) {
 	    	s += " \n";
@@ -344,6 +347,18 @@ public class ImageInfo implements PlugIn {
 			if (cal.calibrated())
 	    		s += " \n";
 	    	s += "No selection\n";
+	    } else if (roi instanceof RotatedRectRoi) {
+	    	s += "\nRotated rectangle selection\n";
+	    	double[] p = ((RotatedRectRoi)roi).getParams();
+			double dx = p[2] - p[0];
+			double dy = p[3] - p[1];
+			double major = Math.sqrt(dx*dx+dy*dy);
+			s += "  Length: " + IJ.d2s(major,2) + "\n";
+			s += "  Width: " + IJ.d2s(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";
 	    } else if (roi instanceof EllipseRoi) {
 	    	s += "\nElliptical selection\n";
 	    	double[] p = ((EllipseRoi)roi).getParams();
diff --git a/ij/plugin/LutLoader.java b/ij/plugin/LutLoader.java
index 0793b4c..be88b8b 100644
--- a/ij/plugin/LutLoader.java
+++ b/ij/plugin/LutLoader.java
@@ -2,6 +2,7 @@ package ij.plugin;
 import ij.*;
 import ij.io.*;
 import ij.process.*;
+import ij.gui.ImageWindow;
 import java.awt.*;
 import java.io.*;
 import java.awt.image.*;
@@ -96,6 +97,8 @@ public class LutLoader extends ImagePlus implements PlugIn {
 				if (imp.getStackSize()>1)
 					imp.getStack().setColorModel(cm);
 				imp.updateAndRepaintWindow();
+				if (IJ.isMacro() && imp.getWindow()!=null)
+					IJ.wait(25);
 			}
 		} else
 			createImage(fi, showImage);
@@ -251,7 +254,7 @@ public class LutLoader extends ImagePlus implements PlugIn {
 	
 	/** Opens an NIH Image LUT, 768 byte binary LUT or text LUT from a file or URL. */
 	boolean openLut(FileInfo fi) {
-		//IJ.showStatus("Opening: " + fi.directory + fi.fileName);
+		//IJ.log("openLut: " + fi.directory + fi.fileName);
 		boolean isURL = fi.url!=null && !fi.url.equals("");
 		int length = 0;
 		String path = isURL?fi.url:fi.directory+fi.fileName;
diff --git a/ij/plugin/Macro_Runner.java b/ij/plugin/Macro_Runner.java
index 899ba0e..d065add 100644
--- a/ij/plugin/Macro_Runner.java
+++ b/ij/plugin/Macro_Runner.java
@@ -48,7 +48,7 @@ public class Macro_Runner implements PlugIn {
 		|| name.endsWith("Menu.ijm") || name.endsWith("Menu.txt"))
 			(new MacroInstaller()).installTool(Menus.getPlugInsPath()+name);
 		else {
-			boolean fullPath = name.startsWith("/") || name.startsWith("\\") || name.indexOf(":\\")==1;
+			boolean fullPath = name.startsWith("/") || name.startsWith("\\") || name.indexOf(":\\")==1 || name.indexOf(":/")==1;
 			if (fullPath)
 				path = name;
 			else
@@ -66,7 +66,7 @@ public class Macro_Runner implements PlugIn {
 		if (arg==null) arg = "";
 		if (name.startsWith("ij.jar:"))
 			return runMacroFromIJJar(name, arg);
-        boolean fullPath = name.startsWith("/") || name.startsWith("\\") || name.indexOf(":\\")==1;
+        boolean fullPath = name.startsWith("/") || name.startsWith("\\") || name.indexOf(":\\")==1 || name.indexOf(":/")==1;
 		String path = name;
 		boolean exists = false;
         if (!fullPath) {
diff --git a/ij/plugin/Options.java b/ij/plugin/Options.java
index 1e56656..75276e7 100644
--- a/ij/plugin/Options.java
+++ b/ij/plugin/Options.java
@@ -41,6 +41,8 @@ public class Options implements PlugIn {
 		gd.addCheckbox("Reverse CZT order of \">\" and \"<\"", Prefs.reverseNextPreviousOrder);
 		if (IJ.isMacOSX())
 			gd.addCheckbox("Don't set Mac menu bar", !Prefs.setIJMenuBar);
+		if (IJ.isLinux())
+			gd.addCheckbox("Save window locations", !Prefs.doNotSaveWindowLocations);
 		gd.addCheckbox("Debug mode", IJ.debugMode);
 		gd.addHelp(IJ.URL+"/docs/menus/edit.html#misc");
 		gd.showDialog();
@@ -73,6 +75,8 @@ public class Options implements PlugIn {
 		Prefs.reverseNextPreviousOrder = gd.getNextBoolean();
 		if (IJ.isMacOSX())
 			Prefs.setIJMenuBar = !gd.getNextBoolean();
+		if (IJ.isLinux())
+			Prefs.doNotSaveWindowLocations = !gd.getNextBoolean();
 		IJ.setDebugMode(gd.getNextBoolean());
 	}
 
@@ -95,7 +99,7 @@ public class Options implements PlugIn {
 		GenericDialog gd = new GenericDialog("I/O Options");
 		gd.addNumericField("JPEG quality (0-100):", FileSaver.getJpegQuality(), 0, 3, "");
 		gd.addNumericField("GIF and PNG transparent index:", Prefs.getTransparentIndex(), 0, 3, "");
-		gd.addStringField("File extension for tables (.txt, .xls or .csv):", Prefs.defaultResultsExtension(), 4);
+		gd.addStringField("File extension for tables (.csv, .tsv or .txt):", Prefs.defaultResultsExtension(), 4);
 		gd.addCheckbox("Use JFileChooser to open/save", Prefs.useJFileChooser);
 		if (!IJ.isMacOSX())
 			gd.addCheckbox("Use_file chooser to import sequences", Prefs.useFileChooser);
@@ -126,7 +130,10 @@ public class Options implements PlugIn {
 		if (!extension.startsWith("."))
 			extension = "." + extension;
 		Prefs.set("options.ext", extension);
+		boolean useJFileChooser2 = Prefs.useJFileChooser;
 		Prefs.useJFileChooser = gd.getNextBoolean();
+		if (Prefs.useJFileChooser!=useJFileChooser2)
+			Prefs.jFileChooserSettingChanged = true;
 		if (!IJ.isMacOSX())
 			Prefs.useFileChooser = gd.getNextBoolean();
 		Prefs.intelByteOrder = gd.getNextBoolean();
diff --git a/ij/plugin/OverlayCommands.java b/ij/plugin/OverlayCommands.java
index 4fc8a68..1a3b67f 100644
--- a/ij/plugin/OverlayCommands.java
+++ b/ij/plugin/OverlayCommands.java
@@ -210,7 +210,6 @@ public class OverlayCommands implements PlugIn {
 			} else
 				roi.setPosition(imp.getCurrentSlice());
 		}
-		//IJ.log(roi.getCPosition()+" "+roi.getZPosition()+" "+roi.getTPosition());
 	}
 
 	void hide() {
diff --git a/ij/plugin/PNM_Writer.java b/ij/plugin/PNM_Writer.java
index 89a5d94..e3a3f13 100644
--- a/ij/plugin/PNM_Writer.java
+++ b/ij/plugin/PNM_Writer.java
@@ -31,34 +31,34 @@ public class PNM_Writer implements PlugIn {
 				ip = ip.duplicate();
 				ip.invert();
 			}
-			ip = ip.convertToByte(true);
+			if (img.getBitDepth()!=16)
+				ip = ip.convertToByte(true);
 			isGray = true;
 			extension = ".pgm";
 		}
 		String title=img.getTitle();
 		int length=title.length();
-		for(int i=2;i<5;i++)
-			if(length>i+1 && title.charAt(length-i)=='.') {
+		for (int i=2;i<5;i++)
+			if (length>i+1 && title.charAt(length-i)=='.') {
 				title=title.substring(0,length-i);
 				break;
 			}
-
 		if (path==null || path.equals("")) {
 			SaveDialog od = new SaveDialog("PNM Writer", title, extension);
 			String dir=od.getDirectory();
 			String name=od.getFileName();
-			if(name==null)
+			if (name==null)
 				return;
 			path = dir + name;
 		}
-
+		IJ.showStatus("Writing PNM "+path+"...");
+		if (img.getBitDepth()==16) {
+			save16BitImage(ip, path);
+			return;
+		}
 		try {
-			IJ.showStatus("Writing PNM "+path+"...");
-			OutputStream fileOutput =
-				new FileOutputStream(path);
-			DataOutputStream output =
-				new DataOutputStream(fileOutput);
-
+			OutputStream fileOutput = new FileOutputStream(path);
+			DataOutputStream output = new DataOutputStream(fileOutput);
 			int w = img.getWidth(), h = img.getHeight();
 			output.writeBytes((isGray ? "P5" : "P6")
 					+ "\n# Written by ImageJ PNM Writer\n"
@@ -87,6 +87,21 @@ public class PNM_Writer implements PlugIn {
 		}
 		IJ.showStatus("");
 	}
+	
+	private void save16BitImage(ImageProcessor ip, String path) {
+		ip.resetMinAndMax();
+		int max = (int)ip.getMax();
+		if (max<256) max=256;
+		try {
+			DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(path)));
+			output.writeBytes("P5\n# Written by ImageJ PNM Writer\n" + ip.getWidth() + " " + ip.getHeight() + "\n"+max+"\n");
+			for (int i=0; i<ip.getPixelCount(); i++)
+				output.writeShort(ip.get(i));
+			output.close();
+		} catch(IOException e) {
+			IJ.handleException(e);
+		}
+	}
 
 };
 
diff --git a/ij/plugin/PointToolOptions.java b/ij/plugin/PointToolOptions.java
index 8f8d52d..b1ec290 100644
--- a/ij/plugin/PointToolOptions.java
+++ b/ij/plugin/PointToolOptions.java
@@ -13,6 +13,7 @@ import java.util.*;
 public class PointToolOptions implements PlugIn, DialogListener {
 	private static GenericDialog gd = null;
 	private boolean multipointTool;
+	private boolean isMacro;
 	
 	private static final String help = "<html>"
 	+"<h1>Point Tool</h1>"
@@ -28,7 +29,7 @@ public class PointToolOptions implements PlugIn, DialogListener {
 	+"</font>";
  
  	public void run(String arg) {
- 		if (gd!=null && gd.isShowing()) {
+ 		if (gd!=null && gd.isShowing() && !IJ.isMacro()) {
  			gd.toFront();
  			update();
  		} else
@@ -37,14 +38,17 @@ public class PointToolOptions implements PlugIn, DialogListener {
 		
 	void showDialog() {
 		String options = IJ.isMacro()?Macro.getOptions():null;
+		isMacro = options!=null;
 		boolean legacyMacro = false;
-		if (options!=null) {
+		if (isMacro) {
 			options = options.replace("selection=", "color=");
 			options = options.replace("marker=", "size=");
 			Macro.setOptions(options);
 			legacyMacro = options.contains("auto-") || options.contains("add");
 		}
 		multipointTool = Toolbar.getMultiPointMode() && !legacyMacro;
+		if (isMacro && !legacyMacro)
+			multipointTool = true;
 		Color sc =Roi.getColor();
 		String sname = Colors.getColorName(sc, "Yellow");
 		Color cc =PointRoi.getDefaultCrossColor();
@@ -85,9 +89,9 @@ public class PointToolOptions implements PlugIn, DialogListener {
 	public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
 		boolean redraw = false;
 		// type
-		int index = gd.getNextChoiceIndex();
-		if (index!=PointRoi.getDefaultType()) {
-			PointRoi.setDefaultType(index);
+		int typeIndex = gd.getNextChoiceIndex();
+		if (typeIndex!=PointRoi.getDefaultType()) {
+			PointRoi.setDefaultType(typeIndex);
 			redraw = true;
 		}
 		// color
@@ -99,9 +103,9 @@ public class PointToolOptions implements PlugIn, DialogListener {
 			Toolbar.getInstance().repaint();
 		}
 		// size
-		index = gd.getNextChoiceIndex();
-		if (index!=PointRoi.getDefaultSize()) {
-			PointRoi.setDefaultSize(index);
+		int sizeIndex = gd.getNextChoiceIndex();
+		if (sizeIndex!=PointRoi.getDefaultSize()) {
+			PointRoi.setDefaultSize(sizeIndex);
 			redraw = true;
 		}
 		if (!multipointTool) {
@@ -132,6 +136,14 @@ public class PointToolOptions implements PlugIn, DialogListener {
 				redraw = true;
 			}
 		}
+		if (isMacro) {
+			PointRoi roi = getPointRoi();
+			if (roi!=null) {
+				roi.setPointType(typeIndex);
+				roi.setStrokeColor(sc);
+				roi.setSize(sizeIndex);
+			}
+		}
 		if (redraw) {
 			ImagePlus imp = null;
 			PointRoi roi = getPointRoi();
diff --git a/ij/plugin/Profiler.java b/ij/plugin/Profiler.java
index 82c806e..e18980e 100644
--- a/ij/plugin/Profiler.java
+++ b/ij/plugin/Profiler.java
@@ -9,11 +9,14 @@ import java.awt.event.*;
 public class Profiler implements PlugIn, PlotMaker {
 	ImagePlus imp;
 	boolean firstTime = true;
+	boolean plotVertically;
 
 	public void run(String arg) {
 		if (arg.equals("set"))
 			{doOptions(); return;}
 		imp = IJ.getImage();
+		if (firstTime)
+			plotVertically = Prefs.verticalProfile || IJ.altKeyDown();
 		Plot plot = getPlot();
 		firstTime = false;
 		if (plot==null)
@@ -24,15 +27,12 @@ public class Profiler implements PlugIn, PlotMaker {
 	
 public Plot getPlot() {
 		Roi roi = imp.getRoi();
-		//if (roi==null && !firstTime)
-		//	IJ.run(imp, "Restore Selection", "");
 		if (roi==null || !(roi.isLine()||roi.getType()==Roi.RECTANGLE)) {
 			if (firstTime)
 				IJ.error("Plot Profile", "Line or rectangular selection required");
 			return null;
 		}
-		boolean averageHorizontally = Prefs.verticalProfile || IJ.altKeyDown();
-		ProfilePlot pp = new ProfilePlot(imp, averageHorizontally);
+		ProfilePlot pp = new ProfilePlot(imp, plotVertically);
 		return pp.getPlot();
 	}
 	
diff --git a/ij/plugin/Projector.java b/ij/plugin/Projector.java
index 00324e2..468bb13 100644
--- a/ij/plugin/Projector.java
+++ b/ij/plugin/Projector.java
@@ -201,7 +201,6 @@ public class Projector implements PlugIn {
 					if ((f==0||!allTimePoints)&& c==0)  {
 						buildImp = projImpD;
 						buildImp.setTitle("BuildStack");
-						//buildImp.show();
 					} else {
 						Concatenator concat = new Concatenator();
 						buildImp =  concat.concatenate(buildImp, projImpD, false);
@@ -215,9 +214,7 @@ public class Projector implements PlugIn {
 			finalSlices = 1;
 		}
 		if (imp.getNChannels()>1)
-			IJ.run( buildImp, 
-				"Stack to Hyperstack...", "order=xyztc channels=" + finalChannels + " slices=" + finalSlices + " frames=" + finalFrames + " display=Composite");
-		//buildImp =  WindowManager.getCurrentImage();
+			buildImp = HyperStackConverter.toHyperStack(buildImp, finalChannels, finalSlices, finalFrames, "xyztc", "composite");
 		if (imp.isComposite()) {
 			CompositeImage buildImp2 = new CompositeImage(buildImp, 0);
 			((CompositeImage)buildImp2).copyLuts(imp);
@@ -226,8 +223,6 @@ public class Projector implements PlugIn {
 		}
 		buildImp.setTitle("Projections of "+imp.getShortTitle());
 		buildImp.show();
-		if (WindowManager.getImage("Concatenated Stacks") != null) 
-				WindowManager.getImage("Concatenated Stacks").hide();
 	}
 
     private  void doRGBProjections(ImagePlus imp) {
diff --git a/ij/plugin/RGBStackMerge.java b/ij/plugin/RGBStackMerge.java
index 1857f71..0e645db 100644
--- a/ij/plugin/RGBStackMerge.java
+++ b/ij/plugin/RGBStackMerge.java
@@ -212,6 +212,8 @@ public class RGBStackMerge implements PlugIn {
 					images[i].close();
 				}
 			}
+			if (imp2.getWindow()!=null)
+				IJ.selectWindow(imp2.getID());
 		}
 	 }
 	 
diff --git a/ij/plugin/Resizer.java b/ij/plugin/Resizer.java
index ecfc047..e69340e 100644
--- a/ij/plugin/Resizer.java
+++ b/ij/plugin/Resizer.java
@@ -219,11 +219,9 @@ public class Resizer implements PlugIn, TextListener, ItemListener  {
 			imp2 = resizeZ(imp, newDepth, interpolationMethod);
 			if (imp2==null)
 				return null;
-			ImageProcessor ip = imp.getProcessor();
-			double min = ip.getMin();
-			double max = ip.getMax();
-			if (bitDepth==16||bitDepth==32)
-				imp2.getProcessor().setMinAndMax(min, max);
+			double min = imp.getDisplayRangeMin();
+			double max = imp.getDisplayRangeMax();
+			imp2.setDisplayRange(min, max);
 		}
 		if (imp2==null)
 			return null;
diff --git a/ij/plugin/RoiRotator.java b/ij/plugin/RoiRotator.java
index 135d4ec..4d3be5a 100644
--- a/ij/plugin/RoiRotator.java
+++ b/ij/plugin/RoiRotator.java
@@ -86,8 +86,6 @@ public class RoiRotator implements PlugIn {
 			poly = new FloatPolygon();
 			poly.addPoint(x1, y1);
 			poly.addPoint(x2, y2);
-			xcenter = x1 + (x2-x1)/2.0;
-			ycenter = y1 + (y2-y1)/2.0;
 		}
 		for (int i=0; i<poly.npoints; i++) {
 			double dx = poly.xpoints[i]-xcenter;
diff --git a/ij/plugin/Scaler.java b/ij/plugin/Scaler.java
index 6c3c70f..20a303d 100644
--- a/ij/plugin/Scaler.java
+++ b/ij/plugin/Scaler.java
@@ -80,6 +80,9 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 		boolean crop = r.width!=imp.getWidth() || r.height!=imp.getHeight();
 		ImageStack stack1 = imp.getStack();
 		ImageStack stack2 = new ImageStack(newWidth, newHeight);
+		boolean virtualStack = stack1.isVirtual();
+		double min = imp.getDisplayRangeMin();
+		double max = imp.getDisplayRangeMax();
 		ImageProcessor ip1, ip2;
 		int method = interpolationMethod;
 		if (w==1 || h==1)
@@ -99,6 +102,8 @@ public class Scaler implements PlugIn, TextListener, FocusListener {
 			IJ.showProgress(i, nSlices);
 		}
 		imp2.setStack(title, stack2);
+		if (virtualStack)
+			imp2.setDisplayRange(min, max);
 		Calibration cal = imp2.getCalibration();
 		if (cal.scaled()) {
 			cal.pixelWidth *= 1.0/xscale;
diff --git a/ij/plugin/ScreenGrabber.java b/ij/plugin/ScreenGrabber.java
index e6a86fd..ac0024a 100644
--- a/ij/plugin/ScreenGrabber.java
+++ b/ij/plugin/ScreenGrabber.java
@@ -9,16 +9,37 @@ import java.awt.*;
     commands may not work on Linux if windows translucency or 
     special effects are enabled in the windows manager. */
 public class ScreenGrabber implements PlugIn {
+	private static int delay = 10;
 
 	public void run(String arg) {
 		ImagePlus imp2 = null;
 		if (arg.equals("image") || arg.equals("flatten"))
 			imp2 = captureImage();
+		else if (arg.equals("delay"))
+			imp2 = captureDelayed();
 		else
 			imp2 = captureScreen();
 		if (imp2!=null)
 			imp2.show();
 	}
+	
+	private ImagePlus captureDelayed() {
+		GenericDialog gd = new GenericDialog("Delayed Capture");
+		gd.addNumericField("Delay (seconds):", delay, 0);
+		gd.showDialog();
+		if (gd.wasCanceled())
+			return null;
+		int delay = (int)gd.getNextNumber();
+		if (delay<0) return null;
+		if (delay>60) delay=60;
+		for (int i=0; i<delay; i++) {
+			IJ.wait(1000);
+			IJ.showStatus("Delayed capture: "+(i+1)+"/"+delay);
+			if (delay>4 && i==delay-2) IJ.beep();
+		}
+		return captureScreen();
+	}
+
     
 	/** Captures the entire screen and returns it as an ImagePlus. */
 	public ImagePlus captureScreen() {
diff --git a/ij/plugin/Selection.java b/ij/plugin/Selection.java
index d5b1dda..3f6235a 100644
--- a/ij/plugin/Selection.java
+++ b/ij/plugin/Selection.java
@@ -12,7 +12,7 @@ import java.awt.event.KeyEvent;
 import java.util.Vector;
 
 
-/** This plugin implements the commands in the Edit/Section submenu. */
+/** This plugin implements the commands in the Edit/Selection submenu. */
 public class Selection implements PlugIn, Measurements {
 	private ImagePlus imp;
 	private float[] kernel = {1f, 1f, 1f, 1f, 1f};
diff --git a/ij/plugin/SimpleCommands.java b/ij/plugin/SimpleCommands.java
index 74ac1de..017183e 100644
--- a/ij/plugin/SimpleCommands.java
+++ b/ij/plugin/SimpleCommands.java
@@ -39,6 +39,8 @@ public class SimpleCommands implements PlugIn {
 			resultsToImage();
 		else if (arg.equals("display"))
 			IJ.runMacroFile("ij.jar:ShowAllLuts", null);
+		else if (arg.equals("missing"))
+			showMissingPluginsMessage();
 		else if (arg.equals("fonts"))
 			showFonts();
 	}
@@ -172,5 +174,14 @@ public class SimpleCommands implements PlugIn {
 		if (ip==null) return;
 		new ImagePlus("Results Table", ip).show();
 	}
+	
+	private void showMissingPluginsMessage() {
+		IJ.showMessage("Path Randomization", 
+			"Plugins were not loaded due to macOS Path Randomization.\n"+
+			"To work around this problem, move ImageJ.app out of the\n"+
+			"ImageJ folder and then copy it back. More information is at\n \n"+
+			IJ.URL+"/docs/install/osx.html#randomization");
+	}
+
 		
 }
diff --git a/ij/plugin/SubstackMaker.java b/ij/plugin/SubstackMaker.java
index 311b25f..aed7a72 100644
--- a/ij/plugin/SubstackMaker.java
+++ b/ij/plugin/SubstackMaker.java
@@ -125,6 +125,9 @@ public class SubstackMaker implements PlugIn {
 	ImagePlus stackList(ImagePlus imp, int count, int[] numList, String stackTitle) throws Exception {
 		ImageStack stack = imp.getStack();
 		ImageStack stack2 = null;
+		boolean virtualStack = stack.isVirtual();
+		double min = imp.getDisplayRangeMin();
+		double max = imp.getDisplayRangeMax();
 		Roi roi = imp.getRoi();
 		for (int i=0, j=0; i<count; i++) {
 			int currSlice = numList[i]-j;
@@ -149,6 +152,8 @@ public class SubstackMaker implements PlugIn {
 		}
 		ImagePlus impSubstack = imp.createImagePlus();
 		impSubstack.setStack(stackTitle, stack2);
+		if (virtualStack)
+			impSubstack.setDisplayRange(min, max);
 		return impSubstack;
 	}
 	
@@ -156,6 +161,9 @@ public class SubstackMaker implements PlugIn {
 	ImagePlus stackRange(ImagePlus imp, int first, int last, int inc, String title) throws Exception {
 		ImageStack stack = imp.getStack();
 		ImageStack stack2 = null;
+		boolean virtualStack = stack.isVirtual();
+		double min = imp.getDisplayRangeMin();
+		double max = imp.getDisplayRangeMax();
 		Roi roi = imp.getRoi();
 		boolean showProgress = stack.getSize()>400 || stack.isVirtual();
 		for (int i= first, j=0; i<= last; i+=inc) {
@@ -183,6 +191,8 @@ public class SubstackMaker implements PlugIn {
 		ImagePlus substack = imp.createImagePlus();
 		substack.setStack(title, stack2);
 		substack.setCalibration(imp.getCalibration());
+		if (virtualStack)
+			substack.setDisplayRange(min, max);
 		return substack;
 	}
 }
diff --git a/ij/plugin/ZProjector.java b/ij/plugin/ZProjector.java
index 78b5302..926b2f5 100644
--- a/ij/plugin/ZProjector.java
+++ b/ij/plugin/ZProjector.java
@@ -379,7 +379,7 @@ public class ZProjector implements PlugIn {
         int c, z, t;
 		for (Roi r : overlay.toArray()) {
 			c = r.getCPosition();
-			z = r.getZPosition();
+			z = r.hasHyperStackPosition()?r.getZPosition():0;
 			t = r.getTPosition();
 			roi = (Roi)r.clone();
 			if (z>=startSlice && z<=stopSlice || z==0 || c==0 || t==0) {
diff --git a/ij/plugin/Zoom.java b/ij/plugin/Zoom.java
index ba1cb59..7acfc7b 100644
--- a/ij/plugin/Zoom.java
+++ b/ij/plugin/Zoom.java
@@ -42,9 +42,11 @@ public class Zoom implements PlugIn{
 			setZoom(imp, ic);
 		else if (arg.equals("max")) {
 			ImageWindow win = imp.getWindow();
-			win.setBounds(win.getMaximumBounds());
-			win.maximize();
-		} if (arg.equals("scale"))
+			if (win!=null) {
+				win.maximize();
+				IJ.wait(100);
+			}
+		} else if (arg.equals("scale"))
 			scaleToFit(imp);
 	}
 	
diff --git a/ij/plugin/filter/Analyzer.java b/ij/plugin/filter/Analyzer.java
index 0c48e77..4c45641 100644
--- a/ij/plugin/filter/Analyzer.java
+++ b/ij/plugin/filter/Analyzer.java
@@ -9,6 +9,7 @@ import ij.measure.*;
 import ij.text.*;
 import ij.plugin.MeasurementsWriter;
 import ij.plugin.Straightener;
+import ij.plugin.frame.RoiManager;
 import ij.util.Tools;
 import ij.macro.Interpreter;
 
@@ -64,8 +65,13 @@ public class Analyzer implements PlugInFilter, Measurements {
 		this.imp = imp;
 	}
 	
+	/** Construct a new Analyzer using an ImagePlus object and a ResultsTable. */
+	public Analyzer(ImagePlus imp, ResultsTable rt) {
+		this(imp, Analyzer.getMeasurements(), rt);
+	}
+
 	/** Construct a new Analyzer using an ImagePlus object and private
-		measurement options and results table. */
+		measurement options and a ResultsTable. */
 	public Analyzer(ImagePlus imp, int measurements, ResultsTable rt) {
 		this.imp = imp;
 		this.measurements = measurements;
@@ -80,11 +86,13 @@ public class Analyzer implements PlugInFilter, Measurements {
 		this.arg = arg;
 		this.imp = imp;
 		IJ.register(Analyzer.class);
-		if (arg.equals("set"))
-			{doSetDialog(); return DONE;}
-		else if (arg.equals("sum"))
-			{summarize(); return DONE;}
-		else if (arg.equals("clear")) {
+		if (arg.equals("set")) {
+			doSetDialog();
+			return DONE;
+		} else if (arg.equals("sum")) {
+			summarize();
+			return DONE;
+		} else if (arg.equals("clear")) {
 			if (IJ.macroRunning())
 				unsavedMeasurements = false;
 			resetCounter();
@@ -688,6 +696,18 @@ public class Analyzer implements PlugInFilter, Measurements {
 			rt.addValue(ResultsTable.MIN_THRESHOLD, stats.lowerThreshold);
 			rt.addValue(ResultsTable.MAX_THRESHOLD, stats.upperThreshold);
 		}
+		if (roi instanceof RotatedRectRoi) {
+			double[] p = ((RotatedRectRoi)roi).getParams();
+			double dx = p[2] - p[0];
+			double dy = p[3] - p[1];
+			double length = Math.sqrt(dx*dx+dy*dy);
+			Calibration cal = imp!=null?imp.getCalibration():null;
+			double pw = 1.0;
+			if (cal!=null && cal.pixelWidth==cal.pixelHeight)
+				pw = cal.pixelWidth;
+			rt.addValue("RRLength", length*pw);
+			rt.addValue("RRWidth", p[4]*pw);
+		}
 	}
 	
 	private void clearSummary() {
@@ -919,6 +939,7 @@ public class Analyzer implements PlugInFilter, Measurements {
 		}
 		umeans = null;
 		systemRT.reset();
+		RoiManager.resetMultiMeasureResults();
 		unsavedMeasurements = false;
 		if (tp!=null) tp.clear();
 		summarized = false;
diff --git a/ij/plugin/filter/ImageMath.java b/ij/plugin/filter/ImageMath.java
index fd1785b..7005fda 100644
--- a/ij/plugin/filter/ImageMath.java
+++ b/ij/plugin/filter/ImageMath.java
@@ -9,7 +9,7 @@ import java.awt.*;
 public class ImageMath implements ExtendedPlugInFilter, DialogListener {
 	
 	public static final String MACRO_KEY = "math.macro";
-	private int flags = DOES_ALL|SUPPORTS_MASKING|KEEP_PREVIEW|PARALLELIZE_STACKS;
+	private int flags = DOES_ALL|SUPPORTS_MASKING|KEEP_PREVIEW;
 	private String arg;
 	private ImagePlus imp;
 	private boolean canceled;	
@@ -31,6 +31,8 @@ public class ImageMath implements ExtendedPlugInFilter, DialogListener {
 		this.arg = arg;
 		this.imp = imp;
 		IJ.register(ImageMath.class);
+		if (!arg.equals("macro") || Interpreter.getInstance()==null)
+			flags |= PARALLELIZE_STACKS;
 		return flags;
 	}
 
diff --git a/ij/plugin/filter/MaximumFinder.java b/ij/plugin/filter/MaximumFinder.java
index a14d3f7..3ec36e8 100644
--- a/ij/plugin/filter/MaximumFinder.java
+++ b/ij/plugin/filter/MaximumFinder.java
@@ -252,29 +252,42 @@ public class MaximumFinder implements ExtendedPlugInFilter, DialogListener {
     }
 
 	/**
-	* Calculates peak positions of 1D array N.Vischer, 13-sep-2013
+	* Calculates peak positions of 1D array N.Vischer, 06-mar-2017
 	*
 	* @param xx Array containing peaks.
 	* @param tolerance Depth of a qualified valley must exceed tolerance.
 	* Tolerance must be >= 0. Flat tops are marked at their centers.
-	* @param  excludeOnEdges If 'true', a peak is only
-	* accepted if it is separated by two qualified valleys. If 'false', a peak
-	* is also accepted if separated by one qualified valley and by a border.
+	* @param  edgeMode 0=include, 1=exclude, 3=circular
+	* edgeMode = 0 (include edges) peak may be separated by one qualified valley and by a border.
+	* edgeMode = 1 (exclude edges) peak must be separated by two qualified valleys
+	* edgeMode = 2 (circular) array is regarded to be circular
 	* @return Positions of peaks, sorted with decreasing amplitude
 	*/
-	public static int[] findMaxima(double[] xx, double tolerance, boolean excludeOnEdges) {
-		boolean includeEdge = !excludeOnEdges;
+	public static int[] findMaxima(double[] xx, double tolerance, int edgeMode ) {
+		final int INCLUDE_EDGE = 0;
+		final int CIRCULAR = 2;
 		int len = xx.length;
+		int origLen = len;
 		if (len<2)
 			return new int[0];
 		if (tolerance < 0)
 			tolerance = 0;
+		if(edgeMode==CIRCULAR){ 
+		    double[] cascade3 = new double[len * 3];
+		    for (int jj = 0; jj <len; jj++){
+			cascade3[jj] = xx[jj];
+			cascade3[jj + len] = xx[jj];
+			cascade3[jj + 2*len] = xx[jj];
+		    }
+		    len *= 3;
+		    xx = cascade3;
+		}
 		int[] maxPositions = new int[len];
 		double max = xx[0];
 		double min = xx[0];
 		int maxPos = 0;
 		int lastMaxPos = -1;
-		boolean leftValleyFound = includeEdge;
+		boolean leftValleyFound = (edgeMode == INCLUDE_EDGE);
 		int maxCount = 0;
 		for (int jj = 1; jj < len; jj++) {
 			double val = xx[jj];
@@ -299,7 +312,7 @@ public class MaximumFinder implements ExtendedPlugInFilter, DialogListener {
 					max = val;
 			}
 		}
-		if (includeEdge) {
+		if (edgeMode == INCLUDE_EDGE) {
 			if (maxCount > 0 && maxPositions[maxCount - 1] != lastMaxPos)
 				maxPositions[maxCount++] = lastMaxPos;
 			if (maxCount == 0 && max - min >= tolerance)
@@ -325,20 +338,42 @@ public class MaximumFinder implements ExtendedPlugInFilter, DialogListener {
 			int pos = maxPositions[rankPositions[jj]];
 			returnArr[maxCount - jj - 1] = pos;//use descending order
 		}
+		if(edgeMode == CIRCULAR){
+		    int count = 0;
+		    for(int jj = 0; jj < returnArr.length;jj++){
+			int pos = returnArr[jj] - origLen;
+			if(pos >= 0 && pos < origLen )//pick maxima from cascade center part
+			    returnArr[count++] = pos;
+		    }
+		    int[] returrn2Arr = new int[count];
+		    System.arraycopy(returnArr, 0, returrn2Arr, 0, count);
+		    returnArr = returrn2Arr;
+		    
+		}
 		return returnArr;
 	}
 	
+	public static int[] findMaxima(double[] xx, double tolerance, boolean excludeOnEdges) {
+	    int edgeBehavior = (excludeOnEdges) ? 1 : 0;
+	    return findMaxima(xx, tolerance, edgeBehavior);
+	}
+	 
 	/**
 	* Returns minimum positions of array xx, sorted with decreasing strength
 	*/
-	public static int[] findMinima(double[] xx, double tolerance, boolean includeEdges) {
+	public static int[] findMinima(double[] xx, double tolerance, boolean excludeEdges ) {
+	    int edgeMode = (excludeEdges) ? 1 : 0;	
+	    return findMinima(xx, tolerance, edgeMode);
+	}
+		
+	public static int[] findMinima(double[] xx, double tolerance, int edgeMode) {
 		int len = xx.length;
 		double[] negArr = new double[len];
 		for (int jj = 0; jj < len; jj++)
 			negArr[jj] = -xx[jj];
-		int[] minPositions = findMaxima(negArr, tolerance, includeEdges);
+		int[] minPositions = findMaxima(negArr, tolerance, edgeMode);
 		return minPositions;
-	}
+	}	
 	
      /** Find the maxima of an image.
      * @param ip             The input image
diff --git a/ij/plugin/filter/ParticleAnalyzer.java b/ij/plugin/filter/ParticleAnalyzer.java
index 663fee2..70d4e3d 100644
--- a/ij/plugin/filter/ParticleAnalyzer.java
+++ b/ij/plugin/filter/ParticleAnalyzer.java
@@ -605,12 +605,8 @@ public class ParticleAnalyzer implements PlugInFilter, Measurements {
 		ip.reset();
 		if (displaySummary && IJ.getInstance()!=null)
 			updateSliceSummary();
-		if (addToManager && roiManager!=null) {
-			if (imp.getWindow()!=null)
-				roiManager.setEditMode(imp, true);
-			else
-				roiManager.runCommand("show all with labels");
-		}
+		if (addToManager && roiManager!=null)
+			roiManager.setEditMode(imp, true);
 		maxParticleCount = (particleCount > maxParticleCount) ? particleCount : maxParticleCount;
 		totalCount += particleCount;
 		if (!canceled)
diff --git a/ij/plugin/frame/ContrastAdjuster.java b/ij/plugin/frame/ContrastAdjuster.java
index 80bbcde..c1431f4 100644
--- a/ij/plugin/frame/ContrastAdjuster.java
+++ b/ij/plugin/frame/ContrastAdjuster.java
@@ -125,7 +125,6 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 		// min slider
 		if (!windowLevel) {
 			minSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
-			GUI.fix(minSlider);
 			c.gridy = y++;
 			c.insets = new Insets(2, 10, 0, 10);
 			gridbag.setConstraints(minSlider, c);
@@ -140,7 +139,6 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 		// max slider
 		if (!windowLevel) {
 			maxSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
-			GUI.fix(maxSlider);
 			c.gridy = y++;
 			c.insets = new Insets(2, 10, 0, 10);
 			gridbag.setConstraints(maxSlider, c);
@@ -154,7 +152,6 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 		
 		// brightness slider
 		brightnessSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
-		GUI.fix(brightnessSlider);
 		c.gridy = y++;
 		c.insets = new Insets(windowLevel?12:2, 10, 0, 10);
 		gridbag.setConstraints(brightnessSlider, c);
@@ -171,7 +168,6 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 		// contrast slider
 		if (!balance) {
 			contrastSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
-			GUI.fix(contrastSlider);
 			c.gridy = y++;
 			c.insets = new Insets(2, 10, 0, 10);
 			gridbag.setConstraints(contrastSlider, c);
@@ -722,7 +718,7 @@ public class ContrastAdjuster extends PlugInDialog implements Runnable,
 		previousImageID = 0;
 	 	((ColorProcessor)ip).caSnapshot(false);
 		setup();
-		imp.deleteRoi();
+		//imp.deleteRoi();
 		if (Recorder.record) {
 			if (Recorder.scriptMode())
 				Recorder.recordCall("IJ.run(imp, \"Apply LUT\", \"\");");
diff --git a/ij/plugin/frame/Editor.java b/ij/plugin/frame/Editor.java
index 28aa89c..0c9951b 100644
--- a/ij/plugin/frame/Editor.java
+++ b/ij/plugin/frame/Editor.java
@@ -16,7 +16,7 @@ import ij.io.SaveDialog;
 
 /** This is a simple TextArea based editor for editing and compiling plugins. */
 public class Editor extends PlugInFrame implements ActionListener, ItemListener,
-	TextListener, ClipboardOwner, MacroConstants, Runnable, Debugger {
+	TextListener, KeyListener, ClipboardOwner, MacroConstants, Runnable, Debugger {
 	
 	/** ImportPackage statements added in front of scripts. Contains no 
 	newlines so that lines numbers in error messages are not changed. */
@@ -40,11 +40,13 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 
 	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;
+	public static final int MACROS_MENU_ITEMS = 13;
 	static final String FONT_SIZE = "editor.font.size";
 	static final String FONT_MONO= "editor.font.mono";
 	static final String CASE_SENSITIVE= "editor.case-sensitive";
 	static final String DEFAULT_DIR= "editor.dir";
+	static final String INSERT_SPACES= "editor.spaces";
+	static final String TAB_INC= "editor.tab-inc";
 	private TextArea ta;
 	private String path;
 	protected boolean changes;
@@ -85,6 +87,10 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
     private ArrayList undoBuffer = new ArrayList();
     private boolean performingUndo;
     private boolean checkForCurlyQuotes;
+    private static int tabInc = (int)Prefs.get(TAB_INC, 3);
+    private static boolean insertSpaces = Prefs.get(INSERT_SPACES, false);
+    CheckboxMenuItem insertSpacesItem;
+
 	
 	public Editor() {
 		this(16, 60, 0, MENU_BAR);
@@ -96,6 +102,7 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		addMenuBar(options);	
 		ta = new TextArea(rows, columns);
 		ta.addTextListener(this);
+		ta.addKeyListener(this);
 		if (IJ.isLinux()) ta.setBackground(Color.white);
  		addKeyListener(IJ.getInstance());  // ImageJ handles keyboard shortcuts
 		add(ta);
@@ -104,8 +111,10 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			fontSize = 0;
 		if (fontSize>=sizes.length)
 			fontSize = sizes.length-1;
-        setFont();
+		setFont();
 		positionWindow();
+		if (IJ.isJava16() && !IJ.isJava18() && !IJ.isLinux())
+			insertSpaces = false;
 	}
 	
 	void addMenuBar(int options) {
@@ -124,9 +133,6 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		mb.add(m);
 		
 		m = new Menu("Edit");
-		//String key = IJ.isMacintosh()?"  Cmd ":"  Ctrl+";
-		//MenuItem item = new MenuItem("Undo"+key+"Z");
-		//item.setEnabled(false);
 		MenuItem item = new MenuItem("Undo",new MenuShortcut(KeyEvent.VK_Z));
 		m.add(item);
 		m.addSeparator();
@@ -156,6 +162,11 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		m.addSeparator();
 		m.add(new MenuItem("Select All", new MenuShortcut(KeyEvent.VK_A)));
 		m.add(new MenuItem("Balance", new MenuShortcut(KeyEvent.VK_B,false)));
+		m.add(new MenuItem("Detab..."));
+		insertSpacesItem = new CheckboxMenuItem("Tab Key Inserts Spaces");
+		insertSpacesItem.addItemListener(this);
+		insertSpacesItem.setState(insertSpaces);
+		m.add(insertSpacesItem);
 		m.add(new MenuItem("Zap Gremlins"));
 		m.add(new MenuItem("Copy to Image Info"));
 		m.addActionListener(this);
@@ -216,6 +227,7 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			macrosMenu.add(new MenuItem("Macro Functions...", new MenuShortcut(KeyEvent.VK_M, true)));
 			macrosMenu.add(new MenuItem("Function Finder...", new MenuShortcut(KeyEvent.VK_F, true)));
 			macrosMenu.addSeparator();
+			macrosMenu.add(new MenuItem("Evaluate Macro"));
 			macrosMenu.add(new MenuItem("Evaluate JavaScript", new MenuShortcut(KeyEvent.VK_J, false)));
 			macrosMenu.add(new MenuItem("Evaluate BeanShell", new MenuShortcut(KeyEvent.VK_B, true)));
 			macrosMenu.add(new MenuItem("Evaluate Python", new MenuShortcut(KeyEvent.VK_P, false)));
@@ -409,6 +421,13 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		new MacroRunner(text, debug?this:null);
 	}
 	
+	void evaluateMacro() {
+		String title = getTitle();
+		if (title.endsWith(".js")||title.endsWith("..bsh")||title.endsWith(".py"))
+			setTitle(title.substring(0,title.length()-3)+".ijm");
+		runMacro(false);
+	}
+
 	void evaluateJavaScript() {
 		if (!getTitle().endsWith(".js"))
 			setTitle(SaveDialog.setExtension(getTitle(), ".js"));
@@ -421,9 +440,18 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			text = ta.getSelectedText();
 		if (text.equals(""))
 			return;
+		boolean strictMode = false;
+		if (IJ.isJava18()) {
+			// text.matches("^( |\t)*(\"use strict\"|'use strict')");
+			String text40 = text.substring(0,Math.min(40,text.length()));
+			strictMode =  text40.contains("'use strict'") || text40.contains("\"use strict\"");
+		}
 		text = getJSPrefix("") + text;
-		if (IJ.isJava18())
+		if (IJ.isJava18()) {
 			text = "load(\"nashorn:mozilla_compat.js\");" + text;
+			if (strictMode)
+				text = "'use strict';" + text;
+		}
 		if ((IJ.isJava16() && !(IJ.isMacOSX()&&!IJ.is64Bit()))) {
 			// Use JavaScript engine built into Java 6 and later.
 			IJ.runPlugIn("ij.plugin.JavaScriptEvaluator", text);
@@ -695,6 +723,8 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			showMacroFunctions();
 		else if ("Function Finder...".equals(what))
 			functionFinder = new FunctionFinder(this);
+		else if ("Evaluate Macro".equals(what))
+			evaluateMacro();
 		else if ("Evaluate JavaScript".equals(what))
 			evaluateJavaScript();
 		else if ("Evaluate BeanShell".equals(what))
@@ -727,6 +757,8 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 			gotoLine();
 		else if ("Balance".equals(what))
 			balance();
+		else if ("Detab...".equals(what))
+			detab();
 		else if ("Zap Gremlins".equals(what))
 			zapGremlins();
 		else if ("Make Text Larger".equals(what))
@@ -889,10 +921,31 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		if (IJ.isMacOSX()) // screen update bug work around
 			ta.setCaretPosition(ta.getCaretPosition());
 	}
+	
+	public void keyPressed(KeyEvent e) { 
+	} 
+	
+	public void keyReleased(KeyEvent e) {
+		int pos = ta.getCaretPosition();
+		if (insertSpaces && pos>0 && e.getKeyCode()==KeyEvent.VK_TAB) {
+			String spaces = " ";
+			for (int i=1; i<tabInc; i++)
+				spaces += " ";
+			ta.replaceRange(spaces, pos-1, pos);
+		}
+	}
+	
+	public void keyTyped(KeyEvent e) {
+	}
 
 	public void itemStateChanged(ItemEvent e) {
 		CheckboxMenuItem item = (CheckboxMenuItem)e.getSource();
-        setFont();
+		String cmd = e.getItem().toString();
+		if ("Tab Key Inserts Spaces".equals(cmd)) {
+			insertSpaces = e.getStateChange()==1;
+			Prefs.set(INSERT_SPACES, insertSpaces);
+		} else
+			setFont();
 	}
 
 	/** Override windowActivated in PlugInFrame to
@@ -1210,6 +1263,54 @@ public class Editor extends PlugInFrame implements ActionListener, ItemListener,
 		else
 			IJ.showMessage("Zap Gremlins", "No invalid characters found");
 	}
+	
+	
+	private void detab() {
+		GenericDialog gd = new GenericDialog("Detab", this);
+		gd.addNumericField("Spaces per tab: ", tabInc, 0);
+		gd.addCheckbox("Tab key inserts spaces: ", insertSpaces);
+		gd.showDialog();
+		if (gd.wasCanceled())
+			return;
+		int tabInc2 = tabInc;
+		tabInc = (int)gd.getNextNumber();
+		if (tabInc<1) tabInc=1;
+		if (tabInc>8) tabInc=8;
+		if (tabInc!=tabInc2)
+			Prefs.set(TAB_INC, tabInc);
+		boolean insertSpaces2 = insertSpaces;
+		insertSpaces = gd.getNextBoolean();
+		if (insertSpaces!=insertSpaces2) {
+			Prefs.set(INSERT_SPACES, insertSpaces);
+			insertSpacesItem.setState(insertSpaces);
+		}
+		int nb = 0;
+		int pos = 1;
+		String text = ta.getText();
+		if (text.indexOf('\t')<0)
+			return;
+		char[] chars = new char[text.length()];
+		chars = text.toCharArray();
+		StringBuffer sb = new StringBuffer((int)(chars.length*1.25));
+		for (int i=0; i<chars.length; i++) {
+			char c = chars[i];
+			if (c=='\t') {
+				nb = tabInc - ((pos-1)%tabInc);
+				while(nb>0) {
+					sb.append(' ');
+					++pos;
+					--nb;
+				}
+			} else if (c=='\n') {
+				sb.append(c);
+				pos = 1;
+			} else {
+				sb.append(c);
+				++pos;
+			}
+		}
+		ta.setText(sb.toString());
+	}
 
 	void selectAll() {
 		ta.selectAll();
diff --git a/ij/plugin/frame/Recorder.java b/ij/plugin/frame/Recorder.java
index 62fb2bd..aab9222 100644
--- a/ij/plugin/frame/Recorder.java
+++ b/ij/plugin/frame/Recorder.java
@@ -555,7 +555,7 @@ public class Recorder extends PlugInFrame implements PlugIn, ActionListener, Ima
 	}
 	
 	static boolean isTextOrTable(String path) {
-		return path.endsWith(".txt") || path.endsWith(".csv") || path.endsWith(".xls");
+		return path.endsWith(".txt") || path.endsWith(".csv") || path.endsWith(".xls") || path.endsWith(".tsv");
 	}
 	
 	static boolean isSaveAs() {
diff --git a/ij/plugin/frame/RoiManager.java b/ij/plugin/frame/RoiManager.java
index 37bb49a..9e8305c 100644
--- a/ij/plugin/frame/RoiManager.java
+++ b/ij/plugin/frame/RoiManager.java
@@ -396,7 +396,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		listModel.addElement(label);
 		roi.setName(label);
 		Roi roiCopy = (Roi)roi.clone();
-		setRoiPosition(imp, roiCopy);
+		roiCopy.setPosition(imp);
 		if (lineWidth>1)
 			roiCopy.setStrokeWidth(lineWidth);
 		if (color!=null)
@@ -568,7 +568,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			if (clone) {
 				String name = (String)listModel.getElementAt(index);
 				Roi roi2 = (Roi)roi.clone();
-				setRoiPosition(imp, roi2);
+				roi2.setPosition(imp);
 				roi.setName(name);
 				roi2.setName(name);
 				rois.set(index, roi2);
@@ -594,7 +594,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		Roi roi = (Roi)rois.get(index);
 		roi.setName(name2);
 		int position = getSliceNumber(name2);
-		if (position>0 && roi.getCPosition()==0 && roi.getZPosition()==0 && roi.getTPosition()==0)
+		if (position>0 && !roi.hasHyperStackPosition())
 			roi.setPosition(position);
 		rois.set(index, roi);
 		listModel.setElementAt(name2, index);
@@ -631,13 +631,9 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		if (imp==null || roi==null)
 			return false;
 		if (setSlice) {
-			int c = roi.getCPosition();
-			int z = roi.getZPosition();
-			int t = roi.getTPosition();
 			boolean hyperstack = imp.isHyperStack();
-			//IJ.log("restore: "+hyperstack+" "+c+" "+z+" "+t);
-			if (hyperstack && (c>0||z>0||t>0))
-				imp.setPosition(c, z, t);
+			if (hyperstack && roi.hasHyperStackPosition())
+				imp.setPosition(roi.getCPosition(), roi.getZPosition(), roi.getTPosition());
 			else {
 				String label = (String)listModel.getElementAt(index);
 				int n = getSliceNumber(roi, label);
@@ -968,7 +964,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			appendResults = cmd.contains("append")?true:false;
 		if (IJ.isMacro()) {
 			if (cmd.startsWith("multi-measure")) {
-				measureAll = cmd.contains(" measure") && nSlices>1;
+				measureAll = cmd.contains(" measure") && nSlices>1; // measure-all
 				onePerSlice = cmd.contains(" one");
 				appendResults = cmd.contains(" append");
 			} else {
@@ -1229,8 +1225,6 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		Font font = null;
 		int justification = TextRoi.LEFT;
 		double opacity = -1;
-		int position = -1;
-		int cpos=-1, zpos=-1, tpos=-1;
 		int pointType = -1;
 		int pointSize = -1;
 		if (showDialog) {
@@ -1254,10 +1248,6 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			color =	 rpRoi.getStrokeColor();
 			fillColor =	 rpRoi.getFillColor();
 			defaultColor = color;
-			position = rpRoi.getPosition();
-			cpos = rpRoi.getCPosition();
-			zpos = rpRoi.getZPosition();
-			tpos = rpRoi.getTPosition();
 			if (rpRoi instanceof TextRoi) {
 				font = ((TextRoi)rpRoi).getCurrentFont();
 				justification = ((TextRoi)rpRoi).getJustification();
@@ -1284,10 +1274,12 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 			if (color!=null) roi.setStrokeColor(color);
 			if (lineWidth>=0) roi.setStrokeWidth(lineWidth);
 			roi.setFillColor(fillColor);
-			if (cpos>0 || zpos>0 || tpos>0)
-				roi.setPosition(cpos, zpos, tpos);
-			else if (position!=-1)
-				roi.setPosition(position);
+			if (rpRoi!=null) {
+				if (rpRoi.hasHyperStackPosition())
+					roi.setPosition(rpRoi.getCPosition(), rpRoi.getZPosition(), rpRoi.getTPosition());
+				else
+					roi.setPosition(rpRoi.getPosition());
+			}
 			if (roi instanceof TextRoi) {
 				roi.setImage(imp);
 				if (font!=null)
@@ -2137,7 +2129,6 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	/** Adds the current selection to the ROI Manager, using the
 		specified color (a 6 digit hex string) and line width. */
 	public boolean runCommand(String cmd, String hexColor, double lineWidth) {
-		//setRoiPosition();
 		if (hexColor==null && lineWidth==1.0 && (IJ.altKeyDown()&&!Interpreter.isBatchMode()))
 			addRoi(true);
 		else {
@@ -2146,16 +2137,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 		}
 		return true;	
 	}
-	
-	private void setRoiPosition(ImagePlus imp, Roi roi) {
-		if (imp==null || roi==null)
-			return;
-		if (imp.isHyperStack())
-			roi.setPosition(imp.getChannel(), imp.getSlice(), imp.getFrame());
-		else if (imp.getStackSize()>1)
-			roi.setPosition(imp.getCurrentSlice());
-	}
-	
+		
 	/** Assigns the ROI at the specified index to the current image. */
 	public void select(int index) {
 		select(null, index);
@@ -2241,7 +2223,7 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
 	public void close() {
 		super.close();
 		instance = null;
-		mmResults = mmResults2 = null;
+		resetMultiMeasureResults();
 		Prefs.saveLocation(LOC_KEY, getLocation());
 		if (!showAllCheckbox.getState() || IJ.macroRunning())
 			return;
@@ -2456,6 +2438,10 @@ public class RoiManager extends PlugInFrame implements ActionListener, ItemListe
     	}
 	}
 	
+	public static void resetMultiMeasureResults() {
+		mmResults = mmResults2 = null;
+	}
+	
 	// This class runs the "Multi Measure" command in a separate thread
 	private class MultiMeasureRunner implements Runnable  {
 		private Thread thread;
diff --git a/ij/plugin/frame/ThresholdAdjuster.java b/ij/plugin/frame/ThresholdAdjuster.java
index 0235f05..7db342f 100644
--- a/ij/plugin/frame/ThresholdAdjuster.java
+++ b/ij/plugin/frame/ThresholdAdjuster.java
@@ -117,7 +117,6 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 
 		// minThreshold slider
 		minSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/3, 1, 0, sliderRange);
-		GUI.fix(minSlider);
 		c.gridx = 0;
 		c.gridy = y++;
 		c.gridwidth = 1;
@@ -142,7 +141,6 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 		
 		// maxThreshold slider
 		maxSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange*2/3, 1, 0, sliderRange);
-		GUI.fix(maxSlider);
 		c.gridx = 0;
 		c.gridy = y++;
 		c.gridwidth = 1;
@@ -405,7 +403,8 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 			else
 				{minThreshold=0; maxThreshold=threshold;}
 		}
-		if (minThreshold>255) minThreshold = 255;
+		if (minThreshold>255)
+			minThreshold = 255;
 		if (Recorder.record) {
 			boolean stack = stackHistogram!=null && stackHistogram.getState();
 			String options = method+(darkb?" dark":"")+(stack?" stack":"");
@@ -417,18 +416,8 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 	}
 	
 	/** Scales threshold levels in the range 0-255 to the actual levels. */
-	void scaleUpAndSet(ImageProcessor ip, double minThreshold, double maxThreshold) {
-		if (!(ip instanceof ByteProcessor) && minThreshold!=ImageProcessor.NO_THRESHOLD) {
-			double min = ip.getMin();
-			double max = ip.getMax();
-			if (max>min) {
-				minThreshold = min + (minThreshold/255.0)*(max-min);
-				maxThreshold = min + (maxThreshold/255.0)*(max-min);
-			} else
-				minThreshold = maxThreshold = min;
-		}
-		ip.setThreshold(minThreshold, maxThreshold, lutColor);
-		//ip.setSnapshotPixels(null); // disable undo removed 20140206 M. Schmid
+	void scaleUpAndSet(ImageProcessor ip, double lower, double upper) {
+		ip.scaleAndSetThreshold(lower, upper, lutColor);
 	}
 
 	/** Scales a threshold level to the range 0-255. */
@@ -525,11 +514,11 @@ public class ThresholdAdjuster extends PlugInDialog implements PlugIn, Measureme
 				max = cal.getCValue((int)max);
 			}
 			if (((int)min==min && (int)max==max) || (ip instanceof ShortProcessor) || max>99999.0) {
-				label1.setText(""+(int)min);
-				label2.setText(""+(int)max);
+				label1.setText(ResultsTable.d2s(min,0));
+				label2.setText(ResultsTable.d2s(max,0));
 			} else {
-				label1.setText(""+IJ.d2s(min,2));
-				label2.setText(""+IJ.d2s(max,2));
+				label1.setText(""+(min<-3.4e38?"-3.4e38":ResultsTable.d2s(min,2)));
+				label2.setText(""+ResultsTable.d2s(max,max==Double.MAX_VALUE?0:2));
 			}
 		}
 	}
diff --git a/ij/process/AutoThresholder.java b/ij/process/AutoThresholder.java
index 1415aa8..395c824 100644
--- a/ij/process/AutoThresholder.java
+++ b/ij/process/AutoThresholder.java
@@ -581,10 +581,9 @@ public class AutoThresholder {
 			Tprev = threshold;
 			temp = (w1+Math.sqrt(sqterm))/w0;
 
-			if (Double.isNaN(temp)) {
-				IJ.log ("MinError(I): NaN, not converging.");
+			if (Double.isNaN(temp))
 				threshold = Tprev;
-			} else
+			else
 				threshold =(int) Math.floor(temp);
 		}
 		return threshold;
diff --git a/ij/process/ByteProcessor.java b/ij/process/ByteProcessor.java
index 8dabef8..37dd30e 100644
--- a/ij/process/ByteProcessor.java
+++ b/ij/process/ByteProcessor.java
@@ -311,7 +311,6 @@ public class ByteProcessor extends ImageProcessor {
 
 	/** Sets the foreground drawing color. */
 	public void setColor(Color color) {
-		//if (ij.IJ.altKeyDown()) throw new IllegalArgumentException("setColor: "+color);
 		drawingColor = color;
 		fgColor = getBestIndex(color);
 	}
diff --git a/ij/process/ColorProcessor.java b/ij/process/ColorProcessor.java
index 456fc97..7cc90ef 100644
--- a/ij/process/ColorProcessor.java
+++ b/ij/process/ColorProcessor.java
@@ -649,7 +649,7 @@ public class ColorProcessor extends ImageProcessor {
 	
 	public static final int RGB_NOISE=0, RGB_MEDIAN=1, RGB_FIND_EDGES=2,
 		RGB_ERODE=3, RGB_DILATE=4, RGB_THRESHOLD=5, RGB_ROTATE=6,
-		RGB_SCALE=7, RGB_RESIZE=8, RGB_TRANSLATE=9;
+		RGB_SCALE=7, RGB_RESIZE=8, RGB_TRANSLATE=9, RGB_MIN=10, RGB_MAX=11;
 
  	/** Performs the specified filter on the red, green and blue planes of this image. */
  	public void filterRGB(int type, double arg) {
@@ -748,6 +748,16 @@ public class ColorProcessor extends ImageProcessor {
 				ij.IJ.showStatus("Translating blue");
 				b.translate(arg, arg2); showProgress(0.90);
 				break;
+			case RGB_MIN:
+				r.filter(MIN); showProgress(0.40);
+				g.filter(MIN); showProgress(0.65);
+				b.filter(MIN); showProgress(0.90);
+				break;
+			case RGB_MAX:
+				r.filter(MAX); showProgress(0.40);
+				g.filter(MAX); showProgress(0.65);
+				b.filter(MAX); showProgress(0.90);
+				break;
 		}
 		
 		R = (byte[])r.getPixels();
@@ -1139,43 +1149,66 @@ public class ColorProcessor extends ImageProcessor {
 		showProgress(1.0);
 	}
 
-	/** 3x3 unweighted smoothing. */
+	/** A 3x3 filter operation, where the argument (ImageProcessor.BLUR_MORE,  FIND_EDGES, 
+	     MEDIAN_FILTER, MIN or MAX) determines the filter type. */
 	public void filter(int type) {
-		int p1, p2, p3, p4, p5, p6, p7, p8, p9;
-		int inc = roiHeight/25;
-		if (inc<1) inc = 1;
+		if (type == FIND_EDGES)
+			filterRGB(RGB_FIND_EDGES, 0, 0);
+		else if (type == MEDIAN_FILTER)
+			filterRGB(RGB_MEDIAN, 0, 0);
+		else if (type == MIN)
+			filterRGB(RGB_MIN, 0, 0);
+		else if (type == MAX)
+			filterRGB(RGB_MAX, 0, 0);
+		else
+		    blurMore();
+	}
+
+	/** BLUR MORE: 3x3 unweighted smoothing is implemented directly, does not convert the image to three ByteProcessors. */
+	private void blurMore() {
+		int p1 = 0, p2, p3, p4 = 0, p5, p6, p7 = 0, p8, p9;
 		
-		int[] pixels2 = (int[])getPixelsCopy();
-		int offset, rsum=0, gsum=0, bsum=0;
-        int rowOffset = width;
-		for (int y=yMin; y<=yMax; y++) {
-			offset = xMin + y * width;
-			p1 = 0;
-			p2 = pixels2[offset-rowOffset-1];
-			p3 = pixels2[offset-rowOffset];
-			p4 = 0;
-			p5 = pixels2[offset-1];
-			p6 = pixels2[offset];
-			p7 = 0;
-			p8 = pixels2[offset+rowOffset-1];
-			p9 = pixels2[offset+rowOffset];
+		int[] prevRow = new int[width];
+		int[] thisRow = new int[width];
+		int[] nextRow = new int[width];
+		System.arraycopy(pixels, Math.max(roiY-1,0)*width, thisRow, 0, width);
+		System.arraycopy(pixels, roiY*width, nextRow, 0, width);
+		for (int y=roiY; y<roiY+roiHeight; y++) {
+			int[] tmp = prevRow;
+			prevRow = thisRow;
+			thisRow = nextRow;
+			nextRow = tmp;
+			if (y < height-1)
+				System.arraycopy(pixels, (y+1)*width, nextRow, 0, width);
+			else
+				nextRow = thisRow;
+			int offset = roiX + y*width;
 
-			for (int x=xMin; x<=xMax; x++) {
+			p2 = prevRow[roiX==0 ? roiX : roiX-1];
+			p3 = prevRow[roiX];
+			p5 = thisRow[roiX==0 ? roiX : roiX-1];
+			p6 = thisRow[roiX];
+			p8 = nextRow[roiX==0 ? roiX : roiX-1];
+			p9 = nextRow[roiX];
+
+			for (int x=roiX; x<roiX+roiWidth; x++) {
 				p1 = p2; p2 = p3;
-				p3 = pixels2[offset-rowOffset+1];
 				p4 = p5; p5 = p6;
-				p6 = pixels2[offset+1];
 				p7 = p8; p8 = p9;
-				p9 = pixels2[offset+rowOffset+1];
-				rsum = (p1 & 0xff0000) + (p2 & 0xff0000) + (p3 & 0xff0000) + (p4 & 0xff0000) + (p5 & 0xff0000)
+				if (x < width-1) {
+					p3 = prevRow[x+1];
+					p6 = thisRow[x+1];
+					p9 = nextRow[x+1];
+				}
+				int rsum = (p1 & 0xff0000) + (p2 & 0xff0000) + (p3 & 0xff0000) + (p4 & 0xff0000) + (p5 & 0xff0000)
 					+ (p6 & 0xff0000) + (p7 & 0xff0000) + (p8 & 0xff0000) + (p9 & 0xff0000);
-				gsum = (p1 & 0xff00) + (p2 & 0xff00) + (p3 & 0xff00) + (p4 & 0xff00) + (p5 & 0xff00)
+				int gsum = (p1 & 0xff00) + (p2 & 0xff00) + (p3 & 0xff00) + (p4 & 0xff00) + (p5 & 0xff00)
 					+ (p6 & 0xff00) + (p7 & 0xff00) + (p8 & 0xff00) + (p9 & 0xff00);
-				bsum = (p1 & 0xff) + (p2 & 0xff) + (p3 & 0xff) + (p4 & 0xff) + (p5 & 0xff)
+				int bsum = (p1 & 0xff) + (p2 & 0xff) + (p3 & 0xff) + (p4 & 0xff) + (p5 & 0xff)
 					+ (p6 & 0xff) + (p7 & 0xff) + (p8 & 0xff) + (p9 & 0xff);
-				pixels[offset++] = 0xff000000 | ((rsum/9) & 0xff0000) | ((gsum/9) & 0xff00) | (bsum/9);
+				pixels[offset++] = 0xff000000 | (((rsum+(4<<16))/9) & 0xff0000) | (((gsum+(4<<8))/9) & 0xff00) | ((bsum+4)/9);
 			}
-			if (y%inc==0)
+			if (roiHeight*roiWidth>1000000 && (y&0xff)==0)
 				showProgress((double)(y-roiY)/roiHeight);
 		}
 		showProgress(1.0);
@@ -1231,6 +1264,14 @@ public class ColorProcessor extends ImageProcessor {
 		return histogram;
 	}
 
+	public synchronized boolean weightedHistogram() {
+		if (weights!=null && (weights[0]!=1d/3d||weights[1]!=1d/3d||weights[2]!=1d/3d))
+			return true;
+		if (rWeight!=1d/3d || gWeight!=1d/3d || bWeight!=1d/3d)
+			return true;
+		return false;
+	}
+
 	/** Performs a convolution operation using the specified kernel. */
 	public void convolve(float[] kernel, int kernelWidth, int kernelHeight) {
 		int size = width*height;
@@ -1282,7 +1323,7 @@ public class ColorProcessor extends ImageProcessor {
 		weights[2] = bWeight;
 		return weights;
 	}
-
+	
 	/** This is a thread-safe (non-static) version of setWeightingFactors(). */
 	public void setRGBWeights(double rweight, double gweight, double bweight) {
 		weights = new double[3];
@@ -1291,6 +1332,11 @@ public class ColorProcessor extends ImageProcessor {
 		weights[2] = bweight;
 	}
 
+	/** This is a thread-safe (non-static) version of setWeightingFactors(). */
+	public void setRGBWeights(double[] weights) {
+		this.weights = weights;
+	}
+	
 	/** Returns the values set by setRGBWeights(), or null if setRGBWeights() has not been called. */
 	public double[] getRGBWeights() {
 		return weights;
diff --git a/ij/process/ImageConverter.java b/ij/process/ImageConverter.java
index 9dafc34..159f2cf 100644
--- a/ij/process/ImageConverter.java
+++ b/ij/process/ImageConverter.java
@@ -81,6 +81,8 @@ public class ImageConverter {
 
 	/** Converts this ImagePlus to RGB. */
 	public void convertToRGB() {
+		if (imp.getBitDepth()==24)
+			return;
 		if (imp.getStackSize()>1) {
 			new StackConverter(imp).convertToRGB();
 			return;
diff --git a/ij/process/ImageProcessor.java b/ij/process/ImageProcessor.java
index 5f96fb8..d8caf83 100644
--- a/ij/process/ImageProcessor.java
+++ b/ij/process/ImageProcessor.java
@@ -7,9 +7,6 @@ import ij.util.*;
 import ij.plugin.filter.GaussianBlur;
 import ij.plugin.Binner;
 import ij.process.AutoThresholder.Method;
-import ij.gui.Roi;
-import ij.gui.ShapeRoi;
-import ij.gui.Overlay;
 import ij.Prefs;
 import ij.measure.Measurements;
 
@@ -164,6 +161,8 @@ public abstract class ImageProcessor implements Cloneable {
 	public void setColorModel(ColorModel cm) {
 		if (cm!=null && !(cm instanceof IndexColorModel))
 			throw new IllegalArgumentException("IndexColorModel required");
+		if (cm!=null && cm instanceof LUT)
+			cm = ((LUT)cm).getColorModel();
 		this.cm = cm;
 		baseCM = null;
 		rLUT1 = rLUT2 = null;
@@ -182,9 +181,13 @@ public abstract class ImageProcessor implements Cloneable {
 	}
 	
 	public void setLut(LUT lut) {
-		setColorModel(lut);
-		if (lut!=null && (lut.min!=0.0||lut.max!=0.0))
-			setMinAndMax(lut.min, lut.max);
+		if (lut==null)
+			setColorModel(null);
+		else {
+			setColorModel(lut.getColorModel());
+			if (lut.min!=0.0 || lut.max!=0.0)
+				setMinAndMax(lut.min, lut.max);
+		}
 	}
 
 
@@ -240,9 +243,15 @@ public abstract class ImageProcessor implements Cloneable {
 		int minDistance = Integer.MAX_VALUE;
 		int distance;
 		int minIndex = 0;
-		int r1=c.getRed();
-		int g1=c.getGreen();
-		int b1=c.getBlue();
+		int r1 = c.getRed();
+		int g1 = c.getGreen();
+		int b1 = c.getBlue();	
+		if (!(r1==g1&&g1==b1&&r1==b1) && icm==defaultColorModel) {
+			double[] w = ColorProcessor.getWeightingFactors();
+			r1 = (int)Math.round(3*r1*w[0]);
+			g1 = (int)Math.round(3*g1*w[1]);
+			b1 = (int)Math.round(3*b1*w[2]);
+		}		
 		int r2,b2,g2;
     	for (int i=0; i<mapSize; i++) {
 			r2 = rLUT[i]&0xff; g2 = gLUT[i]&0xff; b2 = bLUT[i]&0xff;
@@ -556,14 +565,7 @@ public abstract class ImageProcessor implements Cloneable {
 				{lower=0.0; upper=threshold;}
 		}
 		if (lower>255) lower = 255;
-		if (notByteData) {
-			if (max>min) {
-				lower = min + (lower/255.0)*(max-min);
-				upper = min + (upper/255.0)*(max-min);
-			} else
-				lower = upper = min;
-		}
-		setThreshold(lower, upper, lutUpdate);
+		scaleAndSetThreshold(lower, upper, lutUpdate);
 	}
 
 	/** Automatically sets the lower and upper threshold levels, where 'method'
@@ -575,14 +577,12 @@ public abstract class ImageProcessor implements Cloneable {
 			throw new IllegalArgumentException("Invalid thresholding method");
 		if (this instanceof ColorProcessor)
 			return;
-		double min=0.0, max=0.0;
 		boolean notByteData = !(this instanceof ByteProcessor);
 		ImageProcessor ip2 = this;
 		if (notByteData) {
 			ImageProcessor mask = ip2.getMask();
 			Rectangle rect = ip2.getRoi();
 			resetMinAndMax();
-			min = getMin(); max = getMax();
 			ip2 = convertToByte(true);
 			ip2.setMask(mask);
 			ip2.setRoi(rect);	
@@ -633,16 +633,33 @@ public abstract class ImageProcessor implements Cloneable {
 			else
 				{lower=0.0; upper=threshold;}
 		}
-		if (notByteData) {
+		scaleAndSetThreshold(lower, upper, lutUpdate);
+
+	}
+	
+	/** Set the threshold using a 0-255 range. */
+	public void scaleAndSetThreshold(double lower, double upper, int lutUpdate) {
+		int bitDepth = getBitDepth();
+		if (bitDepth!=8 && lower!=NO_THRESHOLD) {
+			double min = getMin();
+			double max = getMax();
 			if (max>min) {
-				lower = min + (lower/255.0)*(max-min);
-				upper = min + (upper/255.0)*(max-min);
+				if (bitDepth==16 && lower==0.0)
+					lower = 0.0;
+				else if (bitDepth==32 && lower==0.0)
+					lower = -Float.MAX_VALUE;
+				else
+					lower = min + (lower/255.0)*(max-min);
+				if (bitDepth==16 && upper==255.0)
+					upper = 65535;
+				else if (bitDepth==32 && upper==255.0)
+					upper = Float.MAX_VALUE;
+				else
+					upper = min + (upper/255.0)*(max-min);
 			} else
 				lower = upper = min;
 		}
 		setThreshold(lower, upper, lutUpdate);
-		//if (notByteData && lutUpdate!=NO_LUT_UPDATE)
-		//	setLutAnimation(true);
 	}
 
 	/** Disables thresholding. */
@@ -962,18 +979,24 @@ public abstract class ImageProcessor implements Cloneable {
     }
 
 	/**
-		Returns an array containing the pixel values along the
-		line starting at (x1,y1) and ending at (x2,y2). For byte
-		and short images, returns calibrated values if a calibration
-		table has been set using setCalibrationTable().
-		@see ImageProcessor#setInterpolate
+	 * Returns an array containing the pixel values along the
+	 * line starting at (x1,y1) and ending at (x2,y2). Pixel
+	 * values are sampled using getInterpolatedValue(double,double)
+	 * if interpolatiion is enabled or getPixelValue(int,int) if it is not.
+	 * For byte and short images, returns calibrated values if a 
+	 * calibration table has been set using setCalibrationTable().
+	 * The length of the returned array, minus one, is approximately 
+	 * equal to the length of the line.
+	 * @see ImageProcessor#setInterpolate
+	 * @see ImageProcessor#getPixelValue
+	 * @see ImageProcessor#getInterpolatedValue
 	*/
 	public double[] getLine(double x1, double y1, double x2, double y2) {
 		double dx = x2-x1;
 		double dy = y2-y1;
 		int n = (int)Math.round(Math.sqrt(dx*dx + dy*dy));
-		double xinc = dx/n;
-		double yinc = dy/n;
+		double xinc = n>0?dx/n:0;
+		double yinc = n>0?dy/n:0;
 		if (!((xinc==0&&n==height) || (yinc==0&&n==width)))
 			n++;
 		double[] data = new double[n];
@@ -986,15 +1009,16 @@ public abstract class ImageProcessor implements Cloneable {
 				ry += yinc;
 			}
 		} else {
+			rx-=0.5; ry-=0.5; 
 			for (int i=0; i<n; i++) {
-				data[i] = getPixelValue((int)(rx+0.5), (int)(ry+0.5));
+				data[i] = getPixelValue((int)Math.round(rx), (int)Math.round(ry));
 				rx += xinc;
 				ry += yinc;
 			}
 		}
 		return data;
 	}
-	
+		
 	/** Returns the pixel values along the horizontal line starting at (x,y). */
 	public void getRow(int x, int y, int[] data, int length) {
 		for (int i=0; i<length; i++)
@@ -1162,6 +1186,13 @@ public abstract class ImageProcessor implements Cloneable {
 			lineTo(x, y);
 		}
 	}
+	
+	/** Fills a rectangle. */
+	public void fillRect(int x, int y, int width, int height) {
+		setRoi(x, y, width, height);
+		fill();
+		resetRoi();
+	}
 
 	/** Draws an elliptical shape. */
 	public void drawOval(int x, int y, int width, int height) {
@@ -1365,7 +1396,7 @@ public abstract class ImageProcessor implements Cloneable {
 	/** Sets the font used by drawString(). */
 	public void setFont(Font font) {
 		this.font = font;
-		fontMetrics	= null;
+		fontMetrics = null;
 		boldFont = font.isBold();
 	}
 	
@@ -1552,7 +1583,7 @@ public abstract class ImageProcessor implements Cloneable {
 
 	/** Draws the specified ROI on this image using the stroke
 		width, stroke color and fill color defined by roi.setStrokeWidth,
-		roi.setStrokeColor() and roi.setFillColor(). Works best with RGB
+		roi.setStrokeColor() and roi.setFillColor(). Works   with RGB
 		images. Does not work with 16-bit and float images.
 		Requires Java 1.6.
 		@see ImageProcessor#draw
@@ -2003,7 +2034,7 @@ public abstract class ImageProcessor implements Cloneable {
     	mean 0.0 and the specified standard deviation, to this image or ROI. */
     public abstract void noise(double standardDeviation);
     
-	/** Creates a new processor containing an image
+	/** Returns a new processor containing an image
 		that corresponds to the current ROI. */
 	public abstract ImageProcessor crop();
 	
@@ -2021,18 +2052,18 @@ public abstract class ImageProcessor implements Cloneable {
 	*/
 	public abstract void scale(double xScale, double yScale);
 	
-	/** Creates a new ImageProcessor containing a scaled copy of this image or ROI.
+	/** Returns a new ImageProcessor containing a scaled copy of this image or ROI.
 		@see ij.process.ImageProcessor#setInterpolate
 	*/
 	public abstract ImageProcessor resize(int dstWidth, int dstHeight);
 	
-	/** Creates a new ImageProcessor containing a scaled copy 
+	/** Returns a new ImageProcessor containing a scaled copy 
 		of this image or ROI, with the aspect ratio maintained. */
 	public ImageProcessor resize(int dstWidth) {
 		return resize(dstWidth, (int)(dstWidth*((double)roiHeight/roiWidth)));
 	}
 
-	/** Creates a new ImageProcessor containing a scaled copy of this image or ROI.
+	/** Returns a new ImageProcessor containing a scaled copy of this image or ROI.
 		@param dstWidth   Image width of the resulting ImageProcessor
 		@param dstHeight  Image height of the resulting ImageProcessor
 		@param useAverging  True means that the averaging occurs to avoid
diff --git a/ij/process/LUT.java b/ij/process/LUT.java
index b04a377..8a69b3b 100644
--- a/ij/process/LUT.java
+++ b/ij/process/LUT.java
@@ -8,6 +8,7 @@ import java.awt.Color;
 		lower and upper bound to be specified. */
     public class LUT extends IndexColorModel implements Cloneable {
         public double min, max;
+        private IndexColorModel cm;
 	
     /** Constructs a LUT from red, green and blue byte arrays, which must have a length of 256. */
     public LUT(byte r[], byte g[], byte b[]) {
@@ -37,7 +38,17 @@ import java.awt.Color;
 	static byte[] getBlues(IndexColorModel cm) {
 		byte[] blues=new byte[256]; cm.getBlues(blues); return blues;
 	}
-	
+
+	public IndexColorModel getColorModel() {
+		if (cm==null) {
+			byte[] reds=new byte[256]; getReds(reds);
+			byte[] greens=new byte[256]; getGreens(greens);
+			byte[] blues=new byte[256]; getBlues(blues);
+			cm = new IndexColorModel(8, getMapSize(), reds, greens, blues);
+		}
+		return cm;
+	}
+		
 	public byte[] getBytes() {
 		int size = getMapSize();
 		if (size!=256) return null;
diff --git a/macros/CommandFinderTool.txt b/macros/CommandFinderTool.txt
new file mode 100644
index 0000000..979ebae
--- /dev/null
+++ b/macros/CommandFinderTool.txt
@@ -0,0 +1,3 @@
+   macro "Command Finder Action Tool - C026O00ddBccL0066" {
+      run("Find Commands...");
+   }
diff --git a/macros/StartupMacros.txt b/macros/StartupMacros.txt
index 7adb066..aa6d561 100644
--- a/macros/StartupMacros.txt
+++ b/macros/StartupMacros.txt
@@ -1,7 +1,8 @@
-// "StartupMacros"
+// Default startup macros
 
+  macro "Command Finder Built-in Tool" {}
   macro "Developer Menu Built-in Tool" {}
-  macro "Stacks Menu Built-in Tool" {}
   macro "Brush Built-in Tool" {}
   macro "Flood Filler Built-in Tool" {}
   macro "Arrow Built-in Tool" {}
+
diff --git a/release-notes.html b/release-notes.html
index 5729f94..32da2e0 100644
--- a/release-notes.html
+++ b/release-notes.html
@@ -5,48 +5,24 @@
 </head>
 <body>
 
-<li> <u>1.51i 16 December 2016</u>
+<li> <u>1.51p 22 June 2017</u>
 <ul>
-<li> Thanks to John Brear, the particle analyzer allows particles to
-be selected on circularity values greater than 1.0.
-<li> Thanks to Johan Doornenbal, the <i>Image>Scale</i> command works
-when Width or Height are not given.
-<li> Thanks to Mirekslouf, labeling of point selections in overlays now
-reflects the state of the "Label points" checkbox in the "Point Tool" dialog. 
-<li> Thanks to Bastian Schmidt, added code to the DICOM importer to handle tags of
-type SS (signed 16-bit), UL (unsigned 32-bit) and SL (signed 32-bit).
-<li> Thanks to Michael Schmid, added static ZAxisProfiler.getPlot(ImagePlus) and
-ZAxisProfiler.getPlot(ImagePlus,String) methods, where the string argument can
-be either "time" or "z-axis".
-<li> Added the ImagePlus.getAllStatistics() method and removed
-the median calculation done in the ImagePlus.getStatistics() method
-in previous versions of ImageJ 1.51.
-<li> Thanks to Benjamen Gyori, added the ImageProcessor.getStats() method,
-which is up to 70 times faster than ImageProcessor.getStatistics().
-<li> Added the FolderOpener.open(path,"virtual") method.
-<li> Thanks to Michael Schmid, added static getNumParams() methods
-to the CurveFitter class.
-<li> Thanks to Gabriel Landini, fixed a bug that caused the <i>Image>Duplicate</i>
-command to not be correctly recorded with single images when the recorder
-was not in "Macro" mode.
-<li> Thanks to Gunjan Pandey, fixed a bug that caused plugins that used the
-ROI Manager to fail when they were called from batch mode macros. Note
-that the ROI Manager is not displayed in batch mode macros unless it is opened
-using run("ROI Manager�") prier to entering batch mode.
-<li> Thanks to Olivier Burri, fixed a bug the caused the "Append results" option
-of the ROI Manager's "Multi Measure" command to be ignored if the
-"One row per slice" option was not enabled.
-<li> Fixed a bug that sometimes caused the doCommand("Point Tool...") macro 
-function to display the legacy point tool dialog instead of the non-modal
-multi-point tool dialog.
-<li> Worked around a OS X/Java 8 bug that caused image windows to
-be maximized when moved if a modal GenericDialog was open.
-<li> Thanks to Norbert Vischer, fixed a v1.50 regression that caused the
-<i>Edit>Selection>Properties</i> command to not work as expected
-with multi-point selections. Use the alt+y shortcut to display
-multi-point selection counts.
-</ul>
-
+<li>  When adding a hyperstack ROI to the ROI Manager, the channel position
+ is set to 0 (display in all channels) if the display mode is "Composite".
+ <li> Added the setOption("ConvertToMicrons",boolean) macro function. When this
+ option is set, units in TIFF images are converted to microns if the pixel width
+ is less than 0.0001 cm.
+ <li> Thanks to Norbert vischer, worked around a bug in Java 8 on OS X that
+ sometimes caused painting of GenericDialogs to be incomplete.
+ <li> Thanks to Rainer Engel, fixed a bug that caused macros that
+ passed variables using "&" notation to fail if they used the
+ <i>Process>Math>Macro</i> command to process a stack.
+ <li> Thanks to Philippe Carl, fixed a 1.51o regression that sometimes
+ caused ImageJ to freeze on Windows when opening or processing
+ image stacks. 
+ </ul>
+ 
 <a href="http://imagej.nih.gov/ij">Home</a>
 </body>
 </html>
+

-- 
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