From 8c5a339e6b7e94f0bef820d52ba863c52c066466 Mon Sep 17 00:00:00 2001 From: Tiago Date: Thu, 27 Oct 2016 11:55:55 -0400 Subject: [PATCH 01/17] Allow ewsc files to be imported See https://github.com/fiji/Simple_Neurite_Tracer/issues/9 --- src/main/java/tracing/NeuriteTracerResultsDialog.java | 2 +- src/main/java/tracing/PathAndFillManager.java | 2 +- src/main/java/tracing/ShollAnalysisPlugin.java | 8 ++++---- src/main/java/tracing/SimpleNeuriteTracer.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index 85dfef75f..44f70e353 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -716,7 +716,7 @@ public NeuriteTracerResultsDialog( String title, menuBar.add(helpMenu()); - loadMenuItem = new JMenuItem("Load traces / SWC file..."); + loadMenuItem = new JMenuItem("Load traces / (e)SWC file..."); loadMenuItem.addActionListener(this); fileMenu.add(loadMenuItem); diff --git a/src/main/java/tracing/PathAndFillManager.java b/src/main/java/tracing/PathAndFillManager.java index 7c5039c3c..4edcf3929 100644 --- a/src/main/java/tracing/PathAndFillManager.java +++ b/src/main/java/tracing/PathAndFillManager.java @@ -1670,7 +1670,7 @@ public boolean importSWC( BufferedReader br, boolean assumeCoordinatesIndexVoxel if( mEmpty.matches() ) continue; String [] fields = line.split("\\s+"); - if( fields.length != 7 ) { + if( fields.length < 7 ) { IJ.error("Wrong number of fields ("+fields.length+") in line: "+line); return false; } diff --git a/src/main/java/tracing/ShollAnalysisPlugin.java b/src/main/java/tracing/ShollAnalysisPlugin.java index 080a0fbd0..b1e394b2c 100644 --- a/src/main/java/tracing/ShollAnalysisPlugin.java +++ b/src/main/java/tracing/ShollAnalysisPlugin.java @@ -106,7 +106,7 @@ public void run(final String ignoredArgument) { imp = (impRequired) ? IJ.openImage(imgPath) : null; if (impRequired && imp == null || !validTracesFile(new File(tracesPath))) { - IJ.error("Invalid image or invalid Traces/SWC file\n \n" + imgPath + "\n" + tracesPath); + IJ.error("Invalid image or invalid Traces/(e)SWC file\n \n" + imgPath + "\n" + tracesPath); return; } @@ -254,7 +254,7 @@ private boolean showDialog() { guessInitialPaths(); gd = new EnhancedGenericDialog("Sholll Analysis (Tracings)..."); - gd.addFileField("Traces/SWC file", tracesPath, 32); + gd.addFileField("Traces/(e)SWC file", tracesPath, 32); gd.addFileField("Image file", imgPath, 32); gd.setInsets(0, 40, 20); gd.addCheckbox("Load tracings without image", !impRequired); @@ -365,7 +365,7 @@ public boolean dialogItemChanged(final GenericDialog arg0, final AWTEvent event) } if (!validTracesFile(new File(tracesPath))) { enableOK = false; - warning += "Not a valid .traces/.swc file"; + warning += "Not a valid .traces/.(e)swc file"; } if (!warning.isEmpty()) { infoMsg.setForeground(Utils.warningColor()); @@ -426,7 +426,7 @@ private boolean expectedImageFile(final File file) { } private boolean tracingsFile(final File file) { - final String[] tracingsExts = new String[] { ".traces", ".swc" }; + final String[] tracingsExts = new String[] { ".traces", ".swc", ".eswc" }; for (final String ext : tracingsExts) if (file.getName().toLowerCase().endsWith(ext)) return true; diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index b0cf7cded..1853377da 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -489,7 +489,7 @@ synchronized public void loadTracings( ) { OpenDialog od; - od = new OpenDialog("Select .traces or .swc file...", + od = new OpenDialog("Select .traces or .(e)swc file...", directory, null ); From 4114f6c66555230e81f067a2c74d298e57f2dcec Mon Sep 17 00:00:00 2001 From: Tiago Date: Mon, 28 Nov 2016 00:40:23 -0500 Subject: [PATCH 02/17] Do not discard overlays when loading image --- src/main/java/tracing/Simple_Neurite_Tracer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/tracing/Simple_Neurite_Tracer.java b/src/main/java/tracing/Simple_Neurite_Tracer.java index 414f271f0..416038fe0 100644 --- a/src/main/java/tracing/Simple_Neurite_Tracer.java +++ b/src/main/java/tracing/Simple_Neurite_Tracer.java @@ -34,6 +34,7 @@ import ij.Macro; import ij.gui.GUI; import ij.gui.GenericDialog; +import ij.gui.Overlay; import ij.gui.YesNoCancelDialog; import ij.measure.Calibration; import ij.plugin.PlugIn; @@ -330,7 +331,9 @@ else if( d.yesPressed() ) { } } + final Overlay currentImageOverlay = currentImage.getOverlay(); initialize(currentImage); + xy.setOverlay(currentImageOverlay); xy_tracer_canvas = (InteractiveTracerCanvas)xy_canvas; xz_tracer_canvas = (InteractiveTracerCanvas)xz_canvas; From 62a995af1b05402aa86bd8a2aa20517d3cc8576b Mon Sep 17 00:00:00 2001 From: Tiago Date: Mon, 28 Nov 2016 14:14:00 -0500 Subject: [PATCH 03/17] Do not reset image overlay when toggling View>MIP Overlay. Closes #12 --- .../java/tracing/SimpleNeuriteTracer.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index 1853377da..b945001f5 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -1610,6 +1610,7 @@ public void clickAtMaxPoint( int x_in_pane, int y_in_pane, int plane ) { } public static final int OVERLAY_OPACITY_PERCENT = 20; + private static final String OVERLAY_IDENTIFIER = "SNT-MIP-OVERLAY"; public void showMIPOverlays(boolean show) { ArrayList allImages = new ArrayList(); @@ -1619,6 +1620,7 @@ public void showMIPOverlays(boolean show) { allImages.add(zy); } for( ImagePlus imagePlus : allImages ) { + Overlay overlayList = imagePlus.getOverlay(); if( show ) { // Create a MIP project of the stack: @@ -1631,16 +1633,27 @@ public void showMIPOverlays(boolean show) { // Add display it as an overlay. // (This logic is taken from OverlayCommands.) Roi roi = new ImageRoi(0, 0, overlay.getProcessor()); - roi.setName(overlay.getShortTitle()); + roi.setName(OVERLAY_IDENTIFIER); ((ImageRoi)roi).setOpacity(OVERLAY_OPACITY_PERCENT/100.0); - Overlay overlayList = imagePlus.getOverlay(); if (overlayList==null) overlayList = new Overlay(); overlayList.add(roi); - imagePlus.setOverlay(overlayList); } else { - imagePlus.setOverlay(null); + removeMIPfromOverlay(overlayList); + } + imagePlus.setOverlay(overlayList); + } + } + + private void removeMIPfromOverlay(Overlay overlay) { + if (overlay != null && overlay.size() > 0) { + for (int i = overlay.size() - 1; i >= 0; i--) { + final String roiName = overlay.get(i).getName(); + if (roiName != null && roiName.equals(OVERLAY_IDENTIFIER)) { + overlay.remove(i); + return; + } } } } From 874ed9d0347dd7b8e2df9aafef260d9064f84bfb Mon Sep 17 00:00:00 2001 From: Tiago Date: Mon, 28 Nov 2016 14:16:39 -0500 Subject: [PATCH 04/17] Do not discard existing ROIs in the overlay when exiting the plugin. Related to #12 --- .../java/tracing/SimpleNeuriteTracer.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index b945001f5..4448cee6c 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -41,6 +41,7 @@ import ij.gui.ImageRoi; import ij.gui.Overlay; import ij.gui.Roi; +import ij.gui.StackWindow; import ij.gui.YesNoCancelDialog; import ij.io.FileInfo; import ij.io.OpenDialog; @@ -58,6 +59,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -1670,4 +1672,26 @@ public boolean getDrawDiametersXY() { return drawDiametersXY; } + @Override + public void closeAndReset() { + // Dispose xz/zy windows unless the user stored some annotations (ROIs) + // on the image overlay + if (!single_pane) { + final List zyxz = Arrays.asList(xz, zy); + for (final ImagePlus imp : zyxz) { + final Overlay overlay = imp.getOverlay(); + removeMIPfromOverlay(overlay); + if (imp.getOverlay().size() == 0) + imp.close(); + } + } + // Restore main view + final Overlay overlay = (xy == null) ? null : xy.getOverlay(); + if (original_xy_canvas != null && xy != null && xy.getImage() != null) { + xy_window = new StackWindow(xy, original_xy_canvas); + removeMIPfromOverlay(overlay); + xy_window.getImagePlus().setOverlay(overlay); + } + } + } From b4b33b70d7641f09f27ec46c04002a1527b94285 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 13:24:26 -0500 Subject: [PATCH 05/17] Allow paths to be exported as ROIs --- src/main/java/tracing/Path.java | 74 +++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/main/java/tracing/Path.java b/src/main/java/tracing/Path.java index b9cfdb8b3..bfc7fd3cb 100644 --- a/src/main/java/tracing/Path.java +++ b/src/main/java/tracing/Path.java @@ -30,8 +30,12 @@ import ij.IJ; import ij.ImagePlus; import ij.ImageStack; +import ij.gui.Overlay; +import ij.gui.PolygonRoi; +import ij.gui.Roi; import ij.gui.StackWindow; import ij.measure.Calibration; +import ij.process.FloatPolygon; import ij.process.FloatProcessor; import ij3d.Content; import ij3d.Image3DUniverse; @@ -833,6 +837,76 @@ public void drawPathAsPoints( TracerCanvas canvas, Graphics g, java.awt.Color c, } } + public void drawPathAsPoints(final Overlay overlay, final java.awt.Color c) { + drawPathAsPoints(overlay, c, ThreePanes.XY_PLANE); + } + + public void drawPathAsPoints(final Overlay overlay, final java.awt.Color c, final int plane) { + + overlay.setStrokeColor(c); + FloatPolygon polygon = new FloatPolygon(); + int current_roi_slice = Integer.MIN_VALUE; + int roi_identifier = 1; + + for (int i = 0; i < points; ++i) { + + double x = Integer.MIN_VALUE; + double y = Integer.MIN_VALUE; + int slice_of_point = Integer.MIN_VALUE; + + switch (plane) { + case ThreePanes.XY_PLANE: + x = getXUnscaledDouble(i); + y = getYUnscaledDouble(i); + slice_of_point = getZUnscaled(i); + break; + case ThreePanes.XZ_PLANE: + x = getXUnscaledDouble(i); + y = getZUnscaledDouble(i); + slice_of_point = getYUnscaled(i); + break; + case ThreePanes.ZY_PLANE: + x = getZUnscaledDouble(i); + y = getYUnscaledDouble(i); + slice_of_point = getXUnscaled(i); + break; + default: + throw new RuntimeException("BUG: Unknown plane! (" + plane + ")"); + } + + if (current_roi_slice == slice_of_point || i == 0) { + polygon.addPoint(x, y); + } else { + addPolyLineToOverlay(polygon, current_roi_slice, roi_identifier++, overlay); + polygon = new FloatPolygon(); // reset ROI + polygon.addPoint(x, y); + } + current_roi_slice = slice_of_point; + + } + + // Create ROI from any remaining points + addPolyLineToOverlay(polygon, current_roi_slice, roi_identifier, overlay); + + } + + private void addPolyLineToOverlay(final FloatPolygon p, final int z_position, final int roi_id, + final Overlay overlay) { + if (p.npoints > 0) { + if (p.npoints == 1) { + // create 1-pixel length lines for single points + p.xpoints[0] -= 0.5f; + p.ypoints[0] -= 0.5f; + p.addPoint(p.xpoints[0] + 0.5f, p.ypoints[0] + 0.5f); + } + final PolygonRoi polyline = new PolygonRoi(p, Roi.FREELINE); + polyline.enableSubPixelResolution(); + // polyline.fitSplineForStraightening(); + polyline.setName(String.format("Path%04d-%04d-Z%d", id, roi_id, z_position)); + polyline.setPosition(z_position + 1); // index 1 + overlay.add(polyline); + } + } public int indexNearestTo( double x, double y, double z ) { From e7c9664f1f7eca058f885d45512da29ebbcd7c47 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 13:44:43 -0500 Subject: [PATCH 06/17] Add methods to export paths as ROIs --- .../java/tracing/SimpleNeuriteTracer.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index 4448cee6c..d465a02fb 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -46,6 +46,7 @@ import ij.io.FileInfo; import ij.io.OpenDialog; import ij.plugin.ZProjector; +import ij.plugin.frame.RoiManager; import ij.process.ByteProcessor; import ij.text.TextWindow; import ij3d.Content; @@ -1460,6 +1461,67 @@ public void setShowOnlySelectedPaths(boolean showOnlySelectedPaths) { repaintAllPanes(); } + public void addPathsToManager(final RoiManager rm) { + if (rm != null) { + final Overlay overlay = new Overlay(); + addAllPathsToOverlay(overlay); + for (final Roi path : overlay.toArray()) + rm.addRoi(path); + rm.runCommand("sort"); + } + } + + public void addAllPathsToOverlay(final Overlay overlay) { + if (overlay != null && pathAndFillManager != null) { + for (int i = 0; i < pathAndFillManager.size(); ++i) { + final Path p = pathAndFillManager.getPath(i); + if (p == null) + continue; + if (p.fittedVersionOf != null) + continue; + // Prefer fitted version when drawing path + final Path drawPath = (p.useFitted) ? p.fitted : p; + drawPath.drawPathAsPoints(overlay, deselectedColor); + } + } + } + + public void addPathsToOverlay() { + if (xy != null) + addPathsToOverlay(xy, ThreePanes.XY_PLANE); + } + + public void addPathsToOverlay(final ImagePlus imp, final int plane) { + + Overlay overlay = imp.getOverlay(); + if (overlay == null) + overlay = new Overlay(); + + if (pathAndFillManager != null) { + for (int i = 0; i < pathAndFillManager.size(); ++i) { + + final Path p = pathAndFillManager.getPath(i); + if (p == null) + continue; + + if (p.fittedVersionOf != null) + continue; + + // If the path suggests using the fitted version, draw that + // instead + final Path drawPath = (p.useFitted) ? p.fitted : p; + + Color color = deselectedColor; + if (pathAndFillManager.isSelected(p)) + color = selectedColor; + else if (showOnlySelectedPaths) + continue; + drawPath.drawPathAsPoints(overlay, color, plane); + } + imp.setOverlay(overlay); + } + } + public boolean getShowOnlySelectedPaths() { return showOnlySelectedPaths; } From 9ddc85599c7c2dba58c8757118a38f396f5e8f85 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 13:50:27 -0500 Subject: [PATCH 07/17] Implement methods to access tracing windows --- src/main/java/tracing/SimpleNeuriteTracer.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index d465a02fb..6a82c497e 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -1522,6 +1522,23 @@ else if (showOnlySelectedPaths) } } + public StackWindow getWindow(final int plane) { + switch (plane) { + case ThreePanes.XY_PLANE: + return xy_window; + case ThreePanes.XZ_PLANE: + return (single_pane) ? null : xz_window; + case ThreePanes.ZY_PLANE: + return (single_pane) ? null : zy_window; + default: + return null; + } + } + + public boolean getSinglePane() { + return single_pane; + } + public boolean getShowOnlySelectedPaths() { return showOnlySelectedPaths; } From a587b76264019fe525ba10b7a29b36ca020480de Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 14:33:39 -0500 Subject: [PATCH 08/17] Freeze tracing operations when main canvas is closed --- .../java/tracing/InteractiveTracerCanvas.java | 3 +- .../tracing/NeuriteTracerResultsDialog.java | 43 +++++++++++++------ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/main/java/tracing/InteractiveTracerCanvas.java b/src/main/java/tracing/InteractiveTracerCanvas.java index fed763391..1d661b855 100644 --- a/src/main/java/tracing/InteractiveTracerCanvas.java +++ b/src/main/java/tracing/InteractiveTracerCanvas.java @@ -184,7 +184,8 @@ public void mouseClicked( MouseEvent e ) { int currentState = tracerPlugin.resultsDialog.getState(); if( currentState == NeuriteTracerResultsDialog.LOADING || - currentState == NeuriteTracerResultsDialog.SAVING ) { + currentState == NeuriteTracerResultsDialog.SAVING || + currentState == NeuriteTracerResultsDialog.IMAGE_CLOSED) { // Do nothing diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index 44f70e353..ed4a6230f 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -121,6 +121,7 @@ public class NeuriteTracerResultsDialog static final int SAVING = 10; static final int LOADING = 11; static final int FITTING_PATHS = 12; + static final int IMAGE_CLOSED = 13; static final String [] stateNames = { "WAITING_TO_START_PATH", "PARTIAL_PATH", @@ -134,7 +135,8 @@ public class NeuriteTracerResultsDialog "WAITING_FOR_SIGMA_CHOICE", "SAVING", "LOADING", - "FITTING_PATHS" }; + "FITTING_PATHS", + "IMAGE CLOSED" }; static final String SEARCHING_STRING = "Searching for path between points..."; @@ -211,10 +213,13 @@ public void run() { // Called when an image is closed @Override - public void imageClosed(ImagePlus imp) { + public void imageClosed(final ImagePlus imp) { SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { updateColorImageChoice(); + if (plugin.getImagePlus() == imp) + changeState(NeuriteTracerResultsDialog.IMAGE_CLOSED); } }); } @@ -455,26 +460,33 @@ protected void exitRequested() { plugin.closeAndReset(); } - protected void disableEverything() { + protected void disableImageDependentComponents() { assert SwingUtilities.isEventDispatchThread(); - - fw.setEnabledNone(); - pw.setButtonsEnabled(false); - - statusText.setEnabled(false); + loadMenuItem.setEnabled(false); + loadLabelsMenuItem.setEnabled(false); keepSegment.setEnabled(false); junkSegment.setEnabled(false); cancelSearch.setEnabled(false); completePath.setEnabled(false); cancelPath.setEnabled(false); - editSigma.setEnabled(false); sigmaWizard.setEnabled(false); + preprocess.setEnabled(false); + useTubularGeodesics.setEnabled(false); + } + + protected void disableEverything() { + assert SwingUtilities.isEventDispatchThread(); + + disableImageDependentComponents(); + + fw.setEnabledNone(); + pw.setButtonsEnabled(false); + + statusText.setEnabled(false); viewPathChoice.setEnabled(false); paths3DChoice.setEnabled(false); - preprocess.setEnabled(false); - useTubularGeodesics.setEnabled(false); exportCSVMenuItem.setEnabled(false); exportAllSWCMenuItem.setEnabled(false); @@ -482,13 +494,10 @@ protected void disableEverything() { sendToTrakEM2.setEnabled(false); analyzeSkeletonMenuItem.setEnabled(false); saveMenuItem.setEnabled(false); - loadMenuItem.setEnabled(false); if( uploadButton != null ) { uploadButton.setEnabled(false); fetchButton.setEnabled(false); } - loadLabelsMenuItem.setEnabled(false); - quitMenuItem.setEnabled(false); } @@ -640,6 +649,12 @@ public void run() { disableEverything(); break; + case IMAGE_CLOSED: + updateStatusText("Tracing image is no longer available..."); + disableImageDependentComponents(); + quitMenuItem.setEnabled(true); + break; + default: IJ.error("BUG: switching to an unknown state"); return; From 94c7b2461a4cfd9063e11bf2a5119b192cedb1a2 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 14:47:22 -0500 Subject: [PATCH 09/17] Implement commands for new export features. While at it, improve GUI: - Allow visibility of tracing panes to be toggle - Disable tracing operations when image is no longer available while allowing analysis of existing traced data --- .../tracing/NeuriteTracerResultsDialog.java | 155 +++++++++++++++--- 1 file changed, 136 insertions(+), 19 deletions(-) diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index ed4a6230f..b2f55a74f 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -28,20 +28,25 @@ package tracing; import sc.fiji.skeletonize3D.Skeletonize3D_; +import stacks.ThreePanes; import features.SigmaPalette; import ij.IJ; import ij.ImageListener; import ij.ImagePlus; +import ij.Prefs; import ij.WindowManager; import ij.gui.GenericDialog; +import ij.gui.StackWindow; import ij.gui.WaitForUserDialog; import ij.gui.YesNoCancelDialog; import ij.io.FileInfo; import ij.io.OpenDialog; import ij.io.SaveDialog; import ij.measure.Calibration; +import ij.plugin.frame.RoiManager; import java.awt.BorderLayout; +import java.awt.Checkbox; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Graphics; @@ -60,6 +65,7 @@ import java.io.File; import java.io.IOException; import java.text.DecimalFormat; +import java.util.Vector; import javax.swing.JButton; import javax.swing.JCheckBox; @@ -100,11 +106,16 @@ public class NeuriteTracerResultsDialog protected JMenuItem analyzeSkeletonMenuItem; protected JMenuItem makeLineStackMenuItem; + protected JMenuItem addPathsToOverlayMenuItem; + protected JMenuItem addPathsToManagerMenuItem; protected JMenuItem exportCSVMenuItemAgain; protected JMenuItem sendToTrakEM2; protected JCheckBoxMenuItem mipOverlayMenuItem; protected JCheckBoxMenuItem drawDiametersXYMenuItem; + protected JCheckBoxMenuItem xyCanvasMenuItem; + protected JCheckBoxMenuItem zyCanvasMenuItem; + protected JCheckBoxMenuItem xzCanvasMenuItem; // These are the states that the UI can be in: @@ -492,6 +503,8 @@ protected void disableEverything() { exportAllSWCMenuItem.setEnabled(false); exportCSVMenuItemAgain.setEnabled(false); sendToTrakEM2.setEnabled(false); + addPathsToManagerMenuItem.setEnabled(false); + addPathsToOverlayMenuItem.setEnabled(false); analyzeSkeletonMenuItem.setEnabled(false); saveMenuItem.setEnabled(false); if( uploadButton != null ) { @@ -539,7 +552,9 @@ public void run() { exportCSVMenuItemAgain.setEnabled(true); sendToTrakEM2.setEnabled(plugin.anyListeners()); analyzeSkeletonMenuItem.setEnabled(true); - if( uploadButton != null ) { + addPathsToManagerMenuItem.setEnabled(true); + addPathsToOverlayMenuItem.setEnabled(true); + if (uploadButton != null) { uploadButton.setEnabled(true); fetchButton.setEnabled(true); } @@ -767,6 +782,15 @@ public NeuriteTracerResultsDialog( String title, makeLineStackMenuItem.addActionListener(this); analysisMenu.add(makeLineStackMenuItem); + analysisMenu.addSeparator(); + addPathsToOverlayMenuItem = new JMenuItem("Add paths to canvas(es) overlay..."); + addPathsToOverlayMenuItem.addActionListener(this); + analysisMenu.add(addPathsToOverlayMenuItem); + addPathsToManagerMenuItem = new JMenuItem("Export paths to ROI Manager"); + addPathsToManagerMenuItem.addActionListener(this); + analysisMenu.add(addPathsToManagerMenuItem); + analysisMenu.addSeparator(); + exportCSVMenuItemAgain = new JMenuItem("Export as CSV..."); exportCSVMenuItemAgain.addActionListener(this); analysisMenu.add(exportCSVMenuItemAgain); @@ -782,6 +806,19 @@ public NeuriteTracerResultsDialog( String title, drawDiametersXYMenuItem.addItemListener(this); viewMenu.add(drawDiametersXYMenuItem); + viewMenu.addSeparator(); + xyCanvasMenuItem = new JCheckBoxMenuItem("Hide XY pane"); + xyCanvasMenuItem.addItemListener(this); + viewMenu.add(xyCanvasMenuItem); + zyCanvasMenuItem = new JCheckBoxMenuItem("Hide ZY pane"); + zyCanvasMenuItem.setEnabled(!plugin.getSinglePane()); + zyCanvasMenuItem.addItemListener(this); + viewMenu.add(zyCanvasMenuItem); + xzCanvasMenuItem = new JCheckBoxMenuItem("Hide XZ pane"); + xzCanvasMenuItem.setEnabled(!plugin.getSinglePane()); + xzCanvasMenuItem.addItemListener(this); + viewMenu.add(xzCanvasMenuItem); + setJMenuBar(menuBar); addWindowListener(this); @@ -1276,30 +1313,80 @@ public void actionPerformed( ActionEvent e ) { plugin.loadLabels(); - } else if( source == makeLineStackMenuItem ) { + } else if (source == makeLineStackMenuItem && !noPathsError()) { - if( pathAndFillManager.size() == 0 ) { - IJ.error("There are no paths traced yet - the stack would be empty"); - } else { - ImagePlus imagePlus = plugin.makePathVolume(); - imagePlus.show(); - } + final ImagePlus imagePlus = plugin.makePathVolume(); + imagePlus.show(); + + } else if (source == analyzeSkeletonMenuItem && !noPathsError()) { + + final ImagePlus imagePlus = plugin.makePathVolume(); + final Skeletonize3D_ skeletonizer = new Skeletonize3D_(); + skeletonizer.setup("", imagePlus); + skeletonizer.run(imagePlus.getProcessor()); + final AnalyzeSkeleton_ analyzer = new AnalyzeSkeleton_(); + analyzer.setup("", imagePlus); + analyzer.run(imagePlus.getProcessor()); + imagePlus.show(); - } else if( source == analyzeSkeletonMenuItem ) { + } else if (source == addPathsToOverlayMenuItem && !noPathsError()) { - if( pathAndFillManager.size() == 0 ) { - IJ.error("There are no paths traced yet!"); + if (plugin.getSinglePane()) { + if (currentState == NeuriteTracerResultsDialog.IMAGE_CLOSED) { + IJ.error("Image is no longer available."); + addPathsToOverlayMenuItem.setEnabled(false); + } else + plugin.addPathsToOverlay(); } else { - ImagePlus imagePlus = plugin.makePathVolume(); - Skeletonize3D_ skeletonizer = new Skeletonize3D_(); - skeletonizer.setup("",imagePlus); - skeletonizer.run(imagePlus.getProcessor()); - AnalyzeSkeleton_ analyzer = new AnalyzeSkeleton_(); - analyzer.setup("",imagePlus); - analyzer.run(imagePlus.getProcessor()); - imagePlus.show(); + final InteractiveTracerCanvas[] canvases = { plugin.xy_tracer_canvas, plugin.xz_tracer_canvas, + plugin.zy_tracer_canvas }; + plugin.xy_tracer_canvas.isShowing(); + final int[] planes = { ThreePanes.XY_PLANE, ThreePanes.XZ_PLANE, ThreePanes.ZY_PLANE }; + final String[] options = { "Main (XY) view", "XZ view", "YZ view" }; + final boolean[] choices = new boolean[3]; + for (int i = 0; i < planes.length; i++) + choices[i] = getImagePlusFromPane(planes[i]) != null; + if (!choices[0] && !choices[1] && !choices[2]) { + IJ.error("Tracing panes are no longer available."); + addPathsToOverlayMenuItem.setEnabled(false); + return; + } + final GenericDialog gd = new GenericDialog("Paths to Overlay"); + gd.addCheckboxGroup(3, 1, options, choices, new String[] { "Add ROI paths to the overlay of:" }); + final Vector cbxs = gd.getCheckboxes(); + for (int i = 0; i < choices.length; i++) + ((Checkbox) cbxs.get(i)).setEnabled(choices[i]); + gd.showDialog(); + if (gd.wasCanceled()) + return; + int i = 0; + for (final InteractiveTracerCanvas canvas : canvases) { + if (gd.getNextBoolean() && canvas != null) + plugin.addPathsToOverlay(canvas.getImage(), planes[i]); + i++; + } } + } else if (source == addPathsToManagerMenuItem && !noPathsError()) { + + RoiManager rm = RoiManager.getInstance2(); + if (rm == null) + rm = new RoiManager(); + if (plugin.singleSlice) + Prefs.showAllSliceOnly = false; + if (rm.getCount() > 0) { + final YesNoCancelDialog d = new YesNoCancelDialog(IJ.getInstance(), "Reset Manager?", + "Delete existing ROIs in the ROI Manager list?"); + if (d.cancelPressed()) + return; + else if (d.yesPressed()) + rm.reset(); + } + rm.setEditMode(plugin.getImagePlus(), false); + plugin.addPathsToManager(rm); + rm.setEditMode(plugin.getImagePlus(), true); + rm.runCommand("show all without labels"); + } else if( source == cancelSearch ) { if( currentState == SEARCHING ) { @@ -1382,6 +1469,29 @@ public void actionPerformed( ActionEvent e ) { } } + private void toggleWindowVisibility(final int pane, final JCheckBoxMenuItem menuItem, final boolean setVisible) { + if (getImagePlusFromPane(pane) == null) { + IJ.error("Image Closed", "Pane is no longer accessible."); + menuItem.setEnabled(false); + menuItem.setSelected(false); + } else { // NB: WindowManager list won't be notified + plugin.getWindow(pane).setVisible(setVisible); + } + } + + private ImagePlus getImagePlusFromPane(final int pane) { + final StackWindow win = plugin.getWindow(pane); + return (win == null) ? null : win.getImagePlus(); + } + + private boolean noPathsError() { + final boolean noPaths = pathAndFillManager.size() == 0; + if (noPaths) { + IJ.error("Simple Neurite Tracer", "There are no traced paths."); + } + return noPaths; + } + @Override public void sigmaPaletteClosing() { SwingUtilities.invokeLater(new Runnable() { @@ -1483,6 +1593,13 @@ public void itemStateChanged( ItemEvent e ) { } else if( source == drawDiametersXYMenuItem ) { plugin.setDrawDiametersXY(e.getStateChange() == ItemEvent.SELECTED); + + } else if (source == xyCanvasMenuItem && xyCanvasMenuItem.isEnabled()) { + toggleWindowVisibility(ThreePanes.XY_PLANE, xyCanvasMenuItem, e.getStateChange() == ItemEvent.DESELECTED); + } else if (source == zyCanvasMenuItem && zyCanvasMenuItem.isEnabled()) { + toggleWindowVisibility(ThreePanes.ZY_PLANE, zyCanvasMenuItem, e.getStateChange() == ItemEvent.DESELECTED); + } else if (source == xzCanvasMenuItem && xzCanvasMenuItem.isEnabled()) { + toggleWindowVisibility(ThreePanes.XZ_PLANE, xzCanvasMenuItem, e.getStateChange() == ItemEvent.DESELECTED); } } From 010a07e2501acecdcfe08c497d0911003a7b1117 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 15:40:56 -0500 Subject: [PATCH 10/17] Allow 'View>Show MIP overlays' to run when side panes have been closed --- src/main/java/tracing/SimpleNeuriteTracer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index 6a82c497e..2f36fef26 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -1701,6 +1701,8 @@ public void showMIPOverlays(boolean show) { allImages.add(zy); } for( ImagePlus imagePlus : allImages ) { + if (imagePlus == null || imagePlus.getImageStackSize() == 1) + continue; Overlay overlayList = imagePlus.getOverlay(); if( show ) { From 699cc136d416bf00ac176e630a4652e78ebf0aa8 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 15:41:57 -0500 Subject: [PATCH 11/17] Properly recover side panes on exit if annotated --- src/main/java/tracing/SimpleNeuriteTracer.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index 2f36fef26..48c41cfa2 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -1758,12 +1758,18 @@ public void closeAndReset() { // Dispose xz/zy windows unless the user stored some annotations (ROIs) // on the image overlay if (!single_pane) { - final List zyxz = Arrays.asList(xz, zy); - for (final ImagePlus imp : zyxz) { - final Overlay overlay = imp.getOverlay(); + final ImagePlus[] impPanes= {xz, zy}; + final StackWindow[] winPanes= {xz_window, zy_window}; + for (int i = 0; i < impPanes.length; ++i) { + final Overlay overlay = impPanes[i].getOverlay(); removeMIPfromOverlay(overlay); - if (imp.getOverlay().size() == 0) - imp.close(); + if (overlay == null || impPanes[i].getOverlay().size() == 0) + impPanes[i].close(); + else { + winPanes[i] = new StackWindow(impPanes[i]); + removeMIPfromOverlay(overlay); + impPanes[i].setOverlay(overlay); + } } } // Restore main view @@ -1771,7 +1777,7 @@ public void closeAndReset() { if (original_xy_canvas != null && xy != null && xy.getImage() != null) { xy_window = new StackWindow(xy, original_xy_canvas); removeMIPfromOverlay(overlay); - xy_window.getImagePlus().setOverlay(overlay); + xy.setOverlay(overlay); } } From 2cdab6b6ddec61d8f4baa76e6c9db0d4a758c2f3 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 15:42:46 -0500 Subject: [PATCH 12/17] Warn when saving unavailable traces --- src/main/java/tracing/NeuriteTracerResultsDialog.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index b2f55a74f..6191c0b86 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -1105,7 +1105,7 @@ public void actionPerformed( ActionEvent e ) { plugin.uploadTracings(); } else if( source == fetchButton ) { plugin.getTracings( true ); - } else */ if( source == saveMenuItem ) { + } else */ if( source == saveMenuItem && !noPathsError()) { FileInfo info = plugin.file_info; SaveDialog sd; @@ -1179,7 +1179,7 @@ public void actionPerformed( ActionEvent e ) { plugin.loadTracings(); changeState( preLoadingState ); - } else if( source == exportAllSWCMenuItem ) { + } else if( source == exportAllSWCMenuItem && !noPathsError()) { FileInfo info = plugin.file_info; SaveDialog sd; @@ -1216,7 +1216,7 @@ public void actionPerformed( ActionEvent e ) { return; pathAndFillManager.exportAllAsSWC( savePath ); - } else if( source == exportCSVMenuItem || source == exportCSVMenuItemAgain ) { + } else if( source == exportCSVMenuItem || source == exportCSVMenuItemAgain && !noPathsError()) { FileInfo info = plugin.file_info; SaveDialog sd; From 3156ec267f192ea93d305593a25ded5cc64aeb30 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 29 Nov 2016 22:02:48 -0500 Subject: [PATCH 13/17] Also recover changes side pane images on exit --- src/main/java/tracing/SimpleNeuriteTracer.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/tracing/SimpleNeuriteTracer.java b/src/main/java/tracing/SimpleNeuriteTracer.java index 48c41cfa2..aa858ff76 100644 --- a/src/main/java/tracing/SimpleNeuriteTracer.java +++ b/src/main/java/tracing/SimpleNeuriteTracer.java @@ -1755,15 +1755,16 @@ public boolean getDrawDiametersXY() { @Override public void closeAndReset() { - // Dispose xz/zy windows unless the user stored some annotations (ROIs) - // on the image overlay + // Dispose xz/zy images unless the user stored some annotations (ROIs) + // on the image overlay or modified them somehow. In that case, restore + // them to the user if (!single_pane) { - final ImagePlus[] impPanes= {xz, zy}; - final StackWindow[] winPanes= {xz_window, zy_window}; + final ImagePlus[] impPanes = { xz, zy }; + final StackWindow[] winPanes = { xz_window, zy_window }; for (int i = 0; i < impPanes.length; ++i) { final Overlay overlay = impPanes[i].getOverlay(); removeMIPfromOverlay(overlay); - if (overlay == null || impPanes[i].getOverlay().size() == 0) + if (!impPanes[i].changes && (overlay == null || impPanes[i].getOverlay().size() == 0)) impPanes[i].close(); else { winPanes[i] = new StackWindow(impPanes[i]); From a7ae6b6ed000931f1383664481cddf74d8c22371 Mon Sep 17 00:00:00 2001 From: Tiago Date: Wed, 30 Nov 2016 10:53:21 -0500 Subject: [PATCH 14/17] Warn when exporting unavailable traces --- src/main/java/tracing/NeuriteTracerResultsDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index 6191c0b86..6d45e300b 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -1216,7 +1216,7 @@ public void actionPerformed( ActionEvent e ) { return; pathAndFillManager.exportAllAsSWC( savePath ); - } else if( source == exportCSVMenuItem || source == exportCSVMenuItemAgain && !noPathsError()) { + } else if( (source == exportCSVMenuItem || source == exportCSVMenuItemAgain) && !noPathsError()) { FileInfo info = plugin.file_info; SaveDialog sd; From c4bdeae0d3df47d50f1662feeafcc5a88c09038f Mon Sep 17 00:00:00 2001 From: Tiago Date: Wed, 30 Nov 2016 10:54:19 -0500 Subject: [PATCH 15/17] Implement the "Arrange planes" command --- .../tracing/NeuriteTracerResultsDialog.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index 6d45e300b..76e96e617 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -53,6 +53,7 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; +import java.awt.Point; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -116,6 +117,8 @@ public class NeuriteTracerResultsDialog protected JCheckBoxMenuItem xyCanvasMenuItem; protected JCheckBoxMenuItem zyCanvasMenuItem; protected JCheckBoxMenuItem xzCanvasMenuItem; + protected JMenuItem arrangeWindowsMenuItem; + // These are the states that the UI can be in: @@ -818,6 +821,10 @@ public NeuriteTracerResultsDialog( String title, xzCanvasMenuItem.setEnabled(!plugin.getSinglePane()); xzCanvasMenuItem.addItemListener(this); viewMenu.add(xzCanvasMenuItem); + viewMenu.addSeparator(); + arrangeWindowsMenuItem = new JMenuItem("Arrange planes"); + arrangeWindowsMenuItem.addActionListener(this); + viewMenu.add(arrangeWindowsMenuItem); setJMenuBar(menuBar); @@ -1466,7 +1473,30 @@ else if (d.yesPressed()) if( ! ignoreColorImageChoiceEvents ) checkForColorImageChange(); + + } else if (source == arrangeWindowsMenuItem) { + arrangeWindows(); + } + } + + private void arrangeWindows() { + final StackWindow xy_window = plugin.getWindow(ThreePanes.XY_PLANE); + if (xy_window == null) + return; + if (!plugin.getSinglePane()) { + final Point loc = xy_window.getLocation(); + final StackWindow zy_window = plugin.getWindow(ThreePanes.ZY_PLANE); + final StackWindow xz_window = plugin.getWindow(ThreePanes.XZ_PLANE); + if (zy_window != null) { + zy_window.setLocation(loc.x + xy_window.getWidth(), loc.y); + zy_window.toFront(); + } + if (xz_window != null) { + xz_window.setLocation(loc.x, loc.y + xy_window.getHeight()); + xz_window.toFront(); + } } + xy_window.toFront(); } private void toggleWindowVisibility(final int pane, final JCheckBoxMenuItem menuItem, final boolean setVisible) { From 7477ab44716f012931f7caf865a0cf47a7bc5453 Mon Sep 17 00:00:00 2001 From: Tiago Date: Wed, 30 Nov 2016 14:13:28 -0500 Subject: [PATCH 16/17] UI tweak: reduce footprint of path list window --- src/main/java/tracing/PathWindow.java | 30 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/java/tracing/PathWindow.java b/src/main/java/tracing/PathWindow.java index 532328d5e..7934d1444 100644 --- a/src/main/java/tracing/PathWindow.java +++ b/src/main/java/tracing/PathWindow.java @@ -32,6 +32,8 @@ import ij.io.SaveDialog; import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -61,6 +63,7 @@ import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; @@ -525,7 +528,7 @@ public PathWindow(PathAndFillManager pathAndFillManager, SimpleNeuriteTracer plu this.pathAndFillManager = pathAndFillManager; this.plugin = plugin; - setBounds(x,y,700,300); + setBounds(x,y,600,240); root = new DefaultMutableTreeNode("All Paths"); tree = new HelpfulJTree(root); // tree.setRootVisible(false); @@ -535,6 +538,7 @@ public PathWindow(PathAndFillManager pathAndFillManager, SimpleNeuriteTracer plu add(scrollPane, BorderLayout.CENTER); buttonPanel = new JPanel(); + buttonPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); add(buttonPanel, BorderLayout.PAGE_END); @@ -577,13 +581,12 @@ public PathWindow(PathAndFillManager pathAndFillManager, SimpleNeuriteTracer plu popup.add(swcTypeMenu); // Create all the menu items: - - renameButton = new JButton("Rename"); - fitVolumeButton = new JButton("Fit Volume"); - fillOutButton = new JButton("Fill Out"); - makePrimaryButton = new JButton("Make Primary"); - deleteButton = new JButton("Delete"); - exportAsSWCButton = new JButton("Export as SWC"); + renameButton = smallButton("Rename"); + fitVolumeButton = smallButton("Fit Volume"); + fillOutButton = smallButton("Fill Out"); + makePrimaryButton = smallButton("Make Primary"); + deleteButton = smallButton("Delete"); + exportAsSWCButton = smallButton("Export as SWC"); buttonPanel.add(renameButton); buttonPanel.add(fitVolumeButton); @@ -616,6 +619,17 @@ protected void maybeShowPopup(MouseEvent me) { tree.addMouseListener(ml); } + private JButton smallButton(final String text) { + final double SCALE = .85; + final JButton button = new JButton(text); + final Font font = button.getFont(); + button.setFont(font.deriveFont((float) (font.getSize() * SCALE))); + final Insets insets = button.getMargin(); + button.setMargin(new Insets((int) (insets.top * SCALE), (int) (insets.left * SCALE), + (int) (insets.bottom * SCALE), (int) (insets.right * SCALE))); + return button; + } + protected void showPopup(MouseEvent me) { assert SwingUtilities.isEventDispatchThread(); // Possibly adjust the selection here: From 85256a7793885d5c258c5fa33003ba8e8b362648 Mon Sep 17 00:00:00 2001 From: Tiago Date: Wed, 30 Nov 2016 14:14:40 -0500 Subject: [PATCH 17/17] UI tweaks: - Reword menu entries - Move Sholl Analysis offline help to Analysis menu - Disable "Arrange plane" command when tracing with a single canvas --- .../tracing/NeuriteTracerResultsDialog.java | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/main/java/tracing/NeuriteTracerResultsDialog.java b/src/main/java/tracing/NeuriteTracerResultsDialog.java index 76e96e617..1031c7c46 100644 --- a/src/main/java/tracing/NeuriteTracerResultsDialog.java +++ b/src/main/java/tracing/NeuriteTracerResultsDialog.java @@ -28,6 +28,7 @@ package tracing; import sc.fiji.skeletonize3D.Skeletonize3D_; +import sholl.Sholl_Analysis; import stacks.ThreePanes; import features.SigmaPalette; import ij.IJ; @@ -36,6 +37,7 @@ import ij.Prefs; import ij.WindowManager; import ij.gui.GenericDialog; +import ij.gui.HTMLDialog; import ij.gui.StackWindow; import ij.gui.WaitForUserDialog; import ij.gui.YesNoCancelDialog; @@ -786,7 +788,7 @@ public NeuriteTracerResultsDialog( String title, analysisMenu.add(makeLineStackMenuItem); analysisMenu.addSeparator(); - addPathsToOverlayMenuItem = new JMenuItem("Add paths to canvas(es) overlay..."); + addPathsToOverlayMenuItem = new JMenuItem("Add paths to overlay..."); addPathsToOverlayMenuItem.addActionListener(this); analysisMenu.add(addPathsToOverlayMenuItem); addPathsToManagerMenuItem = new JMenuItem("Export paths to ROI Manager"); @@ -794,6 +796,8 @@ public NeuriteTracerResultsDialog( String title, analysisMenu.add(addPathsToManagerMenuItem); analysisMenu.addSeparator(); + analysisMenu.add(shollAnalysisHelpMenuItem()); + analysisMenu.addSeparator(); exportCSVMenuItemAgain = new JMenuItem("Export as CSV..."); exportCSVMenuItemAgain.addActionListener(this); analysisMenu.add(exportCSVMenuItemAgain); @@ -810,19 +814,20 @@ public NeuriteTracerResultsDialog( String title, viewMenu.add(drawDiametersXYMenuItem); viewMenu.addSeparator(); - xyCanvasMenuItem = new JCheckBoxMenuItem("Hide XY pane"); + xyCanvasMenuItem = new JCheckBoxMenuItem("Hide XY plane"); xyCanvasMenuItem.addItemListener(this); viewMenu.add(xyCanvasMenuItem); - zyCanvasMenuItem = new JCheckBoxMenuItem("Hide ZY pane"); + zyCanvasMenuItem = new JCheckBoxMenuItem("Hide ZY plane"); zyCanvasMenuItem.setEnabled(!plugin.getSinglePane()); zyCanvasMenuItem.addItemListener(this); viewMenu.add(zyCanvasMenuItem); - xzCanvasMenuItem = new JCheckBoxMenuItem("Hide XZ pane"); + xzCanvasMenuItem = new JCheckBoxMenuItem("Hide XZ plane"); xzCanvasMenuItem.setEnabled(!plugin.getSinglePane()); xzCanvasMenuItem.addItemListener(this); viewMenu.add(xzCanvasMenuItem); viewMenu.addSeparator(); arrangeWindowsMenuItem = new JMenuItem("Arrange planes"); + arrangeWindowsMenuItem.setEnabled(!plugin.getSinglePane()); arrangeWindowsMenuItem.addActionListener(this); viewMenu.add(arrangeWindowsMenuItem); @@ -1696,9 +1701,20 @@ private JMenu helpMenu() { mi = menuItemTrigerringURL("List of shortcuts", URL + ":_Key_Shortcuts"); helpMenu.add(mi); helpMenu.addSeparator(); - mi = menuItemTrigerringURL("Sholl analysis: Online help", URL + ":_Sholl_analysis"); + mi = menuItemTrigerringURL("Sholl analysis walkthrough", URL + ":_Sholl_analysis"); + helpMenu.add(mi); + helpMenu.addSeparator(); + mi = menuItemTrigerringURL("Ask a question", "http://forum.imagej.net"); + helpMenu.add(mi); + helpMenu.addSeparator(); + mi = menuItemTrigerringURL("Citing SNT...", URL + "#Citing_Simple_Neurite_Tracer"); helpMenu.add(mi); - mi = new JMenuItem("Sholl analysis: Offline help"); + return helpMenu; + } + + private JMenuItem shollAnalysisHelpMenuItem() { + JMenuItem mi; + mi = new JMenuItem("Sholl Analysis..."); mi.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { @@ -1707,26 +1723,29 @@ public void actionPerformed(final ActionEvent e) { public void run() { String modKey = IJ.isMacOSX() ? "Alt" : "Ctrl"; modKey += "+Shift"; - final String instructions = "To manually select the center of analysis:\n" - + " 1. Mouse over the path of interest and press \"G\" to activate it\n" - + " 2. Press \"" + modKey + "\" to select a point along the path\n" - + " 3. Press \"" + modKey + "+A\" to initiate Sholl analysis\n \n" - + "For batch processing run \"Sholl Analysis (Tracings)...\"."; - final WaitForUserDialog wd = new WaitForUserDialog("Sholl Analysis Cheat Sheet", instructions); - wd.show(); + final String url1 = Sholl_Analysis.URL + "#Analysis_of_Traced_Cells"; + final String url2 = "http://imagej.net/Simple_Neurite_Tracer/:_Sholl_analysis"; + final StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append("
"); + sb.append("To initiate Sholl Analysis, "); + sb.append("you must first select a focal point:"); + sb.append("
    "); + sb.append("
  1. Mouse over the path of interest. Press \"G\" to activate it
  2. "); + sb.append("
  3. Press \"").append(modKey).append("\" to select a point along the path
  4. "); + sb.append("
  5. Press \"").append(modKey).append("+A\" to start analysis
  6. "); + sb.append("
"); + sb.append("A detailed walkthrough is also available online. "); + sb.append("For batch processing, run Analyze>Sholl>Sholl Analysis (Tracings).... "); + new HTMLDialog("Sholl Analysis How-to", sb.toString(), false); } }); newThread.start(); } }); - helpMenu.add(mi); - helpMenu.addSeparator(); - mi = menuItemTrigerringURL("Ask a question", "http://forum.imagej.net"); - helpMenu.add(mi); - helpMenu.addSeparator(); - mi = menuItemTrigerringURL("Citing SNT...", URL + "#Citing_Simple_Neurite_Tracer"); - helpMenu.add(mi); - return helpMenu; + return mi; } public static JMenuItem menuItemTrigerringURL(final String label, final String URL) {