Search in sources :

Example 31 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Display method actionPerformed.

@Override
public void actionPerformed(final ActionEvent ae) {
    dispatcher.exec(new Runnable() {

        @Override
        public void run() {
            final String command = ae.getActionCommand();
            if (command.startsWith("Job")) {
                if (Utils.checkYN("Really cancel job?")) {
                    project.getLoader().quitJob(command);
                    repairGUI();
                }
                return;
            } else if (command.equals("Move to top")) {
                if (null == active)
                    return;
                canvas.setUpdateGraphics(true);
                getLayerSet().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.TOP, active);
                getLayerSet().addUndoMoveStep(active);
                Display.repaint(layer.getParent(), active, 5);
            // Display.updatePanelIndex(layer, active);
            } else if (command.equals("Move up")) {
                if (null == active)
                    return;
                canvas.setUpdateGraphics(true);
                getLayerSet().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.UP, active);
                getLayerSet().addUndoMoveStep(active);
                Display.repaint(layer.getParent(), active, 5);
            // Display.updatePanelIndex(layer, active);
            } else if (command.equals("Move down")) {
                if (null == active)
                    return;
                canvas.setUpdateGraphics(true);
                getLayerSet().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.DOWN, active);
                getLayerSet().addUndoMoveStep(active);
                Display.repaint(layer.getParent(), active, 5);
            // Display.updatePanelIndex(layer, active);
            } else if (command.equals("Move to bottom")) {
                if (null == active)
                    return;
                canvas.setUpdateGraphics(true);
                getLayerSet().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.BOTTOM, active);
                getLayerSet().addUndoMoveStep(active);
                Display.repaint(layer.getParent(), active, 5);
            // Display.updatePanelIndex(layer, active);
            } else if (command.equals("Duplicate, link and send to next layer")) {
                duplicateLinkAndSendTo(active, 1, layer.getParent().next(layer));
            } else if (command.equals("Duplicate, link and send to previous layer")) {
                duplicateLinkAndSendTo(active, 0, layer.getParent().previous(layer));
            } else if (command.equals("Duplicate, link and send to...")) {
                // fix non-scrolling popup menu
                Utils.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        final GenericDialog gd = new GenericDialog("Send to");
                        gd.addMessage("Duplicate, link and send to...");
                        final String[] sl = new String[layer.getParent().size()];
                        int next = 0;
                        for (final Layer la : layer.getParent().getLayers()) {
                            sl[next++] = project.findLayerThing(la).toString();
                        }
                        gd.addChoice("Layer: ", sl, sl[layer.getParent().indexOf(layer)]);
                        gd.showDialog();
                        if (gd.wasCanceled())
                            return;
                        final Layer la = layer.getParent().getLayer(gd.getNextChoiceIndex());
                        if (layer == la) {
                            Utils.showMessage("Can't duplicate, link and send to the same layer.");
                            return;
                        }
                        duplicateLinkAndSendTo(active, 0, la);
                    }
                });
            } else if (-1 != command.indexOf("z = ")) {
                // this is an item from the "Duplicate, link and send to" menu of layer z's
                final Layer target_layer = layer.getParent().getLayer(Double.parseDouble(command.substring(command.lastIndexOf(' ') + 1)));
                Utils.log2("layer: __" + command.substring(command.lastIndexOf(' ') + 1) + "__");
                if (null == target_layer)
                    return;
                duplicateLinkAndSendTo(active, 0, target_layer);
            } else if (-1 != command.indexOf("z=")) {
                // WARNING the indexOf is very similar to the previous one
                // Send the linked group to the selected layer
                final int iz = command.indexOf("z=") + 2;
                Utils.log2("iz=" + iz + "  other: " + command.indexOf(' ', iz + 2));
                int end = command.indexOf(' ', iz);
                if (-1 == end)
                    end = command.length();
                final double lz = Double.parseDouble(command.substring(iz, end));
                final Layer target = layer.getParent().getLayer(lz);
                // TODO what happens when ZDisplayable are selected?
                layer.getParent().move(selection.getAffected(), active.getLayer(), target);
            } else if (command.equals("Unlink")) {
                if (null == active || active instanceof Patch)
                    return;
                active.unlink();
                // selection.update();
                updateSelection();
            } else if (command.equals("Unlink from images")) {
                if (null == active)
                    return;
                try {
                    for (final Displayable displ : selection.getSelected()) {
                        displ.unlinkAll(Patch.class);
                    }
                    // selection.update();
                    updateSelection();
                } catch (final Exception e) {
                    IJError.print(e);
                }
            } else if (command.equals("Unlink slices")) {
                final YesNoCancelDialog yn = new YesNoCancelDialog(frame, "Attention", "Really unlink all slices from each other?\nThere is no undo.");
                if (!yn.yesPressed())
                    return;
                final ArrayList<Patch> pa = ((Patch) active).getStackPatches();
                for (int i = pa.size() - 1; i > 0; i--) {
                    pa.get(i).unlink(pa.get(i - 1));
                }
            } else if (command.equals("Send to next layer")) {
                final Rectangle box = selection.getBox();
                try {
                    // unlink Patch instances
                    for (final Displayable displ : selection.getSelected()) {
                        displ.unlinkAll(Patch.class);
                    }
                    // selection.update();
                    updateSelection();
                } catch (final Exception e) {
                    IJError.print(e);
                }
                // layer.getParent().moveDown(layer, active); // will repaint whatever appropriate layers
                selection.moveDown();
                repaint(layer.getParent(), box);
            } else if (command.equals("Send to previous layer")) {
                final Rectangle box = selection.getBox();
                try {
                    // unlink Patch instances
                    for (final Displayable displ : selection.getSelected()) {
                        displ.unlinkAll(Patch.class);
                    }
                    // selection.update();
                    updateSelection();
                } catch (final Exception e) {
                    IJError.print(e);
                }
                // layer.getParent().moveUp(layer, active); // will repaint whatever appropriate layers
                selection.moveUp();
                repaint(layer.getParent(), box);
            } else if (command.equals("Show centered")) {
                if (active == null)
                    return;
                showCentered(active);
            } else if (command.equals("Delete...")) {
                // remove all selected objects
                selection.deleteAll();
            } else if (command.equals("Color...")) {
                IJ.doCommand("Color Picker...");
            } else if (command.equals("Revert")) {
                if (null == active || active.getClass() != Patch.class)
                    return;
                final Patch p = (Patch) active;
                if (!p.revert()) {
                    if (null == p.getOriginalPath())
                        Utils.log("No editions to save for patch " + p.getTitle() + " #" + p.getId());
                    else
                        Utils.log("Could not revert Patch " + p.getTitle() + " #" + p.getId());
                }
            } else if (command.equals("Remove alpha mask")) {
                Display.removeAlphaMasks(selection.get(Patch.class));
            } else if (command.equals("Undo")) {
                Bureaucrat.createAndStart(new Worker.Task("Undo") {

                    @Override
                    public void exec() {
                        layer.getParent().undoOneStep();
                        Display.repaint(layer.getParent());
                    }
                }, project);
            } else if (command.equals("Redo")) {
                Bureaucrat.createAndStart(new Worker.Task("Redo") {

                    @Override
                    public void exec() {
                        layer.getParent().redoOneStep();
                        Display.repaint(layer.getParent());
                    }
                }, project);
            } else if (command.equals("Apply transform")) {
                canvas.applyTransform();
            } else if (command.equals("Apply transform propagating to last layer")) {
                if (mode.getClass() == AffineTransformMode.class || mode.getClass() == NonLinearTransformMode.class) {
                    final LayerSet ls = getLayerSet();
                    // +1 to exclude current layer
                    final HashSet<Layer> subset = new HashSet<Layer>(ls.getLayers(ls.indexOf(Display.this.layer) + 1, ls.size() - 1));
                    if (mode.getClass() == AffineTransformMode.class)
                        ((AffineTransformMode) mode).applyAndPropagate(subset);
                    else if (mode.getClass() == NonLinearTransformMode.class)
                        ((NonLinearTransformMode) mode).apply(subset);
                    setMode(new DefaultMode(Display.this));
                }
            } else if (command.equals("Apply transform propagating to first layer")) {
                if (mode.getClass() == AffineTransformMode.class || mode.getClass() == NonLinearTransformMode.class) {
                    final LayerSet ls = getLayerSet();
                    // -1 to exclude current layer
                    final HashSet<Layer> subset = new HashSet<Layer>(ls.getLayers(0, ls.indexOf(Display.this.layer) - 1));
                    if (mode.getClass() == AffineTransformMode.class)
                        ((AffineTransformMode) mode).applyAndPropagate(subset);
                    else if (mode.getClass() == NonLinearTransformMode.class)
                        ((NonLinearTransformMode) mode).apply(subset);
                    setMode(new DefaultMode(Display.this));
                }
            } else if (command.equals("Cancel transform")) {
                // calls getMode().cancel()
                canvas.cancelTransform();
            } else if (command.equals("Specify transform...")) {
                if (null == active)
                    return;
                selection.specify();
            } else if (command.equals("Exit inspection")) {
                getMode().cancel();
                setMode(new DefaultMode(Display.this));
            } else if (command.equals("Inspect image mesh triangles")) {
                setMode(new InspectPatchTrianglesMode(Display.this));
            } else if (command.equals("Hide all but images")) {
                final ArrayList<Class<?>> type = new ArrayList<Class<?>>();
                type.add(Patch.class);
                type.add(Stack.class);
                final Collection<Displayable> col = layer.getParent().hideExcept(type, false);
                selection.removeAll(col);
                Display.updateCheckboxes(col, DisplayablePanel.VISIBILITY_STATE);
                Display.update(layer.getParent(), false);
            } else if (command.equals("Unhide all")) {
                Display.updateCheckboxes(layer.getParent().setAllVisible(false), DisplayablePanel.VISIBILITY_STATE);
                Display.update(layer.getParent(), false);
            } else if (command.startsWith("Hide all ")) {
                // skip the ending plural 's'
                final String type = command.substring(9, command.length() - 1);
                final Collection<Displayable> col = layer.getParent().setVisible(type, false, true);
                selection.removeAll(col);
                Display.updateCheckboxes(col, DisplayablePanel.VISIBILITY_STATE);
            } else if (command.startsWith("Unhide all ")) {
                // skip the ending plural 's'
                String type = command.substring(11, command.length() - 1);
                type = type.substring(0, 1).toUpperCase() + type.substring(1);
                updateCheckboxes(layer.getParent().setVisible(type, true, true), DisplayablePanel.VISIBILITY_STATE);
            } else if (command.equals("Hide deselected")) {
                hideDeselected(0 != (ActionEvent.ALT_MASK & ae.getModifiers()));
            } else if (command.equals("Hide deselected except images")) {
                hideDeselected(true);
            } else if (command.equals("Hide selected")) {
                // TODO should deselect them too? I don't think so.
                selection.setVisible(false);
                Display.updateCheckboxes(selection.getSelected(), DisplayablePanel.VISIBILITY_STATE);
            } else if (command.equals("Resize canvas/LayerSet...")) {
                resizeCanvas();
            } else if (command.equals("Autoresize canvas/LayerSet")) {
                layer.getParent().setMinimumDimensions();
            } else if (command.equals("Resize canvas/LayerSet to ROI")) {
                final Roi roi = canvas.getFakeImagePlus().getRoi();
                if (null == roi) {
                    Utils.log("No ROI present!");
                    return;
                }
                resizeCanvas(roi.getBounds());
            } else if (command.equals("Import image")) {
                importImage();
            } else if (command.equals("Import next image")) {
                importNextImage();
            } else if (command.equals("Import stack...")) {
                Display.this.getLayerSet().addChangeTreesStep();
                final Rectangle sr = getCanvas().getSrcRect();
                final Bureaucrat burro = project.getLoader().importStack(layer, sr.x + sr.width / 2, sr.y + sr.height / 2, null, true, null, false);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        Display.this.getLayerSet().addChangeTreesStep();
                    }
                });
            } else if (command.equals("Import stack with landmarks...")) {
                // 1 - Find out if there's any other project open
                final List<Project> pr = Project.getProjects();
                if (1 == pr.size()) {
                    Utils.logAll("Need another project open!");
                    return;
                }
                // 2 - Ask for a "landmarks" type
                final GenericDialog gd = new GenericDialog("Landmarks");
                gd.addStringField("landmarks type:", "landmarks");
                final String[] none = { "-- None --" };
                final Hashtable<String, Project> mpr = new Hashtable<String, Project>();
                for (final Project p : pr) {
                    if (p == project)
                        continue;
                    mpr.put(p.toString(), p);
                }
                final String[] project_titles = mpr.keySet().toArray(new String[0]);
                final Hashtable<String, ProjectThing> map_target = findLandmarkNodes(project, "landmarks");
                final String[] target_landmark_titles = map_target.isEmpty() ? none : map_target.keySet().toArray(new String[0]);
                gd.addChoice("Landmarks node in this project:", target_landmark_titles, target_landmark_titles[0]);
                gd.addMessage("");
                gd.addChoice("Source project:", project_titles, project_titles[0]);
                final Hashtable<String, ProjectThing> map_source = findLandmarkNodes(mpr.get(project_titles[0]), "landmarks");
                final String[] source_landmark_titles = map_source.isEmpty() ? none : map_source.keySet().toArray(new String[0]);
                gd.addChoice("Landmarks node in source project:", source_landmark_titles, source_landmark_titles[0]);
                final List<Patch> stacks = Display.getPatchStacks(mpr.get(project_titles[0]).getRootLayerSet());
                String[] stack_titles;
                if (stacks.isEmpty()) {
                    if (1 == mpr.size()) {
                        IJ.showMessage("Project " + project_titles[0] + " does not contain any Stack.");
                        return;
                    }
                    stack_titles = none;
                } else {
                    stack_titles = new String[stacks.size()];
                    int next = 0;
                    for (final Patch pa : stacks) stack_titles[next++] = pa.toString();
                }
                gd.addChoice("Stacks:", stack_titles, stack_titles[0]);
                final Vector<?> vc = gd.getChoices();
                final Choice choice_target_landmarks = (Choice) vc.get(0);
                final Choice choice_source_projects = (Choice) vc.get(1);
                final Choice choice_source_landmarks = (Choice) vc.get(2);
                final Choice choice_stacks = (Choice) vc.get(3);
                final TextField input = (TextField) gd.getStringFields().get(0);
                input.addTextListener(new TextListener() {

                    @Override
                    public void textValueChanged(final TextEvent te) {
                        final String text = input.getText();
                        update(choice_target_landmarks, Display.this.project, text, map_target);
                        update(choice_source_landmarks, mpr.get(choice_source_projects.getSelectedItem()), text, map_source);
                    }

                    private void update(final Choice c, final Project p, final String type, final Hashtable<String, ProjectThing> table) {
                        table.clear();
                        table.putAll(findLandmarkNodes(p, type));
                        c.removeAll();
                        if (table.isEmpty())
                            c.add(none[0]);
                        else
                            for (final String t : table.keySet()) c.add(t);
                    }
                });
                choice_source_projects.addItemListener(new ItemListener() {

                    @Override
                    public void itemStateChanged(final ItemEvent e) {
                        final String item = (String) e.getItem();
                        final Project p = mpr.get(choice_source_projects.getSelectedItem());
                        // 1 - Update choice of landmark items
                        map_source.clear();
                        map_source.putAll(findLandmarkNodes(p, input.getText()));
                        choice_target_landmarks.removeAll();
                        if (map_source.isEmpty())
                            choice_target_landmarks.add(none[0]);
                        else
                            for (final String t : map_source.keySet()) choice_target_landmarks.add(t);
                        // 2 - Update choice of Stack items
                        stacks.clear();
                        choice_stacks.removeAll();
                        stacks.addAll(Display.getPatchStacks(mpr.get(project_titles[0]).getRootLayerSet()));
                        if (stacks.isEmpty())
                            choice_stacks.add(none[0]);
                        else
                            for (final Patch pa : stacks) choice_stacks.add(pa.toString());
                    }
                });
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final String type = gd.getNextString();
                if (null == type || 0 == type.trim().length()) {
                    Utils.log("Invalid landmarks node type!");
                    return;
                }
                final ProjectThing target_landmarks_node = map_target.get(gd.getNextChoice());
                final Project source = mpr.get(gd.getNextChoice());
                final ProjectThing source_landmarks_node = map_source.get(gd.getNextChoice());
                final Patch stack_patch = stacks.get(gd.getNextChoiceIndex());
                // Store current state
                Display.this.getLayerSet().addLayerContentStep(layer);
                // Insert stack
                insertStack(target_landmarks_node, source, source_landmarks_node, stack_patch);
                // Store new state
                Display.this.getLayerSet().addChangeTreesStep();
            } else if (command.equals("Import grid...")) {
                Display.this.getLayerSet().addLayerContentStep(layer);
                final Bureaucrat burro = project.getLoader().importGrid(layer);
                if (null != burro)
                    burro.addPostTask(new Runnable() {

                        @Override
                        public void run() {
                            Display.this.getLayerSet().addLayerContentStep(layer);
                        }
                    });
            } else if (command.equals("Import sequence as grid...")) {
                Display.this.getLayerSet().addChangeTreesStep();
                final Bureaucrat burro = project.getLoader().importSequenceAsGrid(layer);
                if (null != burro)
                    burro.addPostTask(new Runnable() {

                        @Override
                        public void run() {
                            Display.this.getLayerSet().addChangeTreesStep();
                        }
                    });
            } else if (command.equals("Import from text file...")) {
                Display.this.getLayerSet().addChangeTreesStep();
                final Bureaucrat burro = project.getLoader().importImages(layer);
                if (null != burro)
                    burro.addPostTask(new Runnable() {

                        @Override
                        public void run() {
                            Display.this.getLayerSet().addChangeTreesStep();
                        }
                    });
            } else if (command.equals("Import labels as arealists...")) {
                Display.this.getLayerSet().addChangeTreesStep();
                final Bureaucrat burro = project.getLoader().importLabelsAsAreaLists(layer, null, Double.MAX_VALUE, 0, 0.4f, false);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        Display.this.getLayerSet().addChangeTreesStep();
                    }
                });
            } else if (command.equals("Make flat image...")) {
                // if there's a ROI, just use that as cropping rectangle
                Rectangle srcRect = null;
                final Roi roi = canvas.getFakeImagePlus().getRoi();
                if (null != roi) {
                    srcRect = roi.getBounds();
                } else {
                    // otherwise, whatever is visible
                    // srcRect = canvas.getSrcRect();
                    // The above is confusing. That is what ROIs are for. So paint all:
                    srcRect = new Rectangle(0, 0, (int) Math.ceil(layer.getParent().getLayerWidth()), (int) Math.ceil(layer.getParent().getLayerHeight()));
                }
                double scale = 1.0;
                final String[] types = new String[] { "8-bit grayscale", "RGB Color" };
                int the_type = ImagePlus.GRAY8;
                final GenericDialog gd = new GenericDialog("Choose", frame);
                gd.addSlider("Scale: ", 1, 100, 100);
                gd.addNumericField("Width: ", srcRect.width, 0);
                gd.addNumericField("height: ", srcRect.height, 0);
                // connect the above 3 fields:
                final Vector<?> numfields = gd.getNumericFields();
                final UpdateDimensionField udf = new UpdateDimensionField(srcRect.width, srcRect.height, (TextField) numfields.get(1), (TextField) numfields.get(2), (TextField) numfields.get(0), (Scrollbar) gd.getSliders().get(0));
                for (final Object ob : numfields) ((TextField) ob).addTextListener(udf);
                gd.addChoice("Type: ", types, types[0]);
                if (layer.getParent().size() > 1) {
                    // / $#%! where are my lisp macros
                    Utils.addLayerRangeChoices(Display.this.layer, gd);
                    gd.addCheckbox("Include non-empty layers only", true);
                }
                gd.addMessage("Background color:");
                Utils.addRGBColorSliders(gd, Color.black);
                gd.addCheckbox("Best quality", false);
                gd.addMessage("");
                final String[] choices = new String[] { "Show", "Save to file", "Save for web (CATMAID)" };
                gd.addChoice("Export:", choices, choices[0]);
                final String[] formats = Saver.formats();
                gd.addChoice("Format:", formats, formats[0]);
                gd.addNumericField("Tile_side", 256, 0);
                final Choice cformats = (Choice) gd.getChoices().get(gd.getChoices().size() - 1);
                cformats.setEnabled(false);
                final Choice cchoices = (Choice) gd.getChoices().get(gd.getChoices().size() - 2);
                final TextField tf = (TextField) gd.getNumericFields().get(gd.getNumericFields().size() - 1);
                tf.setEnabled(false);
                cchoices.addItemListener(new ItemListener() {

                    @Override
                    public void itemStateChanged(final ItemEvent e) {
                        cformats.setEnabled(cchoices.getSelectedIndex() > 0);
                        if (2 == cchoices.getSelectedIndex()) {
                            cformats.select(".jpg");
                            tf.setEnabled(true);
                        } else {
                            tf.setEnabled(false);
                        }
                    }
                });
                gd.addCheckbox("Use original images", true);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                scale = gd.getNextNumber() / 100;
                the_type = (0 == gd.getNextChoiceIndex() ? ImagePlus.GRAY8 : ImagePlus.COLOR_RGB);
                if (Double.isNaN(scale) || scale <= 0.0) {
                    Utils.showMessage("Invalid scale.");
                    return;
                }
                // consuming and ignoring width and height:
                gd.getNextNumber();
                gd.getNextNumber();
                Layer[] layer_array = null;
                boolean non_empty_only = false;
                if (layer.getParent().size() > 1) {
                    non_empty_only = gd.getNextBoolean();
                    final int i_start = gd.getNextChoiceIndex();
                    final int i_end = gd.getNextChoiceIndex();
                    final ArrayList<Layer> al = new ArrayList<Layer>();
                    final ArrayList<ZDisplayable> al_zd = layer.getParent().getZDisplayables();
                    final ZDisplayable[] zd = new ZDisplayable[al_zd.size()];
                    al_zd.toArray(zd);
                    for (int i = i_start, j = 0; i <= i_end; i++, j++) {
                        final Layer la = layer.getParent().getLayer(i);
                        // checks both the Layer and the ZDisplayable objects in the parent LayerSet
                        if (!la.isEmpty() || !non_empty_only)
                            al.add(la);
                    }
                    if (0 == al.size()) {
                        Utils.showMessage("All layers are empty!");
                        return;
                    }
                    layer_array = new Layer[al.size()];
                    al.toArray(layer_array);
                } else {
                    layer_array = new Layer[] { Display.this.layer };
                }
                final Color background = new Color((int) gd.getNextNumber(), (int) gd.getNextNumber(), (int) gd.getNextNumber());
                final boolean quality = gd.getNextBoolean();
                final int choice = gd.getNextChoiceIndex();
                final boolean save_to_file = 1 == choice;
                final boolean save_for_web = 2 == choice;
                final String format = gd.getNextChoice();
                final Saver saver = new Saver(format);
                final int tile_side = (int) gd.getNextNumber();
                final boolean use_original_images = gd.getNextBoolean();
                // in its own thread
                if (save_for_web)
                    project.getLoader().makePrescaledTiles(layer_array, Patch.class, srcRect, scale, c_alphas, the_type, null, use_original_images, saver, tile_side);
                else
                    project.getLoader().makeFlatImage(layer_array, srcRect, scale, c_alphas, the_type, save_to_file, format, quality, background);
            } else if (command.equals("Lock")) {
                selection.setLocked(true);
                Utils.revalidateComponent(tabs.getSelectedComponent());
            } else if (command.equals("Unlock")) {
                selection.setLocked(false);
                Utils.revalidateComponent(tabs.getSelectedComponent());
            } else if (command.equals("Properties...")) {
                switch(selection.getSelected().size()) {
                    case 0:
                        return;
                    case 1:
                        active.adjustProperties();
                        break;
                    default:
                        adjustGroupProperties(selection.getSelected());
                        break;
                }
                updateSelection();
            } else if (command.equals("Measurement options...")) {
                adjustMeasurementOptions();
            } else if (command.equals("Show current 2D position in 3D")) {
                final Point p = canvas.consumeLastPopupPoint();
                if (null == p)
                    return;
                Display3D.addFatPoint("Current 2D Position", getLayerSet(), p.x, p.y, layer.getZ(), 10, Color.magenta);
            } else if (command.equals("Show layers as orthoslices in 3D")) {
                final GenericDialog gd = new GenericDialog("Options");
                final Roi roi = canvas.getFakeImagePlus().getRoi();
                final Rectangle r = null == roi ? getLayerSet().get2DBounds() : roi.getBounds();
                gd.addMessage("ROI 2D bounds:");
                gd.addNumericField("x:", r.x, 0, 30, "pixels");
                gd.addNumericField("y:", r.y, 0, 30, "pixels");
                gd.addNumericField("width:", r.width, 0, 30, "pixels");
                gd.addNumericField("height:", r.height, 0, 30, "pixels");
                gd.addMessage("Layers to include:");
                Utils.addLayerRangeChoices(layer, gd);
                gd.addMessage("Constrain dimensions to:");
                gd.addNumericField("max width and height:", getLayerSet().getPixelsMaxDimension(), 0, 30, "pixels");
                gd.addMessage("Options:");
                final String[] types = { "Greyscale", "Color RGB" };
                gd.addChoice("Image type:", types, types[0]);
                gd.addCheckbox("Invert images", false);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final int x = (int) gd.getNextNumber(), y = (int) gd.getNextNumber(), width = (int) gd.getNextNumber(), height = (int) gd.getNextNumber();
                final int first = gd.getNextChoiceIndex(), last = gd.getNextChoiceIndex();
                final List<Layer> layers = getLayerSet().getLayers(first, last);
                final int max_dim = Math.min((int) gd.getNextNumber(), Math.max(width, height));
                float scale = 1;
                if (max_dim < Math.max(width, height)) {
                    scale = max_dim / (float) Math.max(width, height);
                }
                final int type = 0 == gd.getNextChoiceIndex() ? ImagePlus.GRAY8 : ImagePlus.COLOR_RGB;
                final boolean invert = gd.getNextBoolean();
                final LayerStack stack = new LayerStack(layers, new Rectangle(x, y, width, height), scale, type, Patch.class, max_dim, invert);
                Display3D.showOrthoslices(stack.getImagePlus(), "LayerSet [" + x + "," + y + "," + width + "," + height + "] " + first + "--" + last, x, y, scale, layers.get(0));
            } else if (command.equals("Align stack slices")) {
                if (getActive() instanceof Patch) {
                    final Patch slice = (Patch) getActive();
                    if (slice.isStack()) {
                        // check linked group
                        final HashSet hs = slice.getLinkedGroup(new HashSet());
                        for (final Iterator it = hs.iterator(); it.hasNext(); ) {
                            if (it.next().getClass() != Patch.class) {
                                // labels should be fine, need to check that
                                Utils.showMessage("Images are linked to other objects, can't proceed to cross-correlate them.");
                                return;
                            }
                        }
                        final LayerSet ls = slice.getLayerSet();
                        final HashSet<Displayable> linked = slice.getLinkedGroup(null);
                        ls.addTransformStepWithData(linked);
                        // will repaint
                        final Bureaucrat burro = AlignTask.registerStackSlices((Patch) getActive());
                        burro.addPostTask(new Runnable() {

                            @Override
                            public void run() {
                                ls.enlargeToFit(linked);
                                // The current state when done
                                ls.addTransformStepWithData(linked);
                            }
                        });
                    } else {
                        Utils.log("Align stack slices: selected image is not part of a stack.");
                    }
                }
            } else if (command.equals("Align layers manually with landmarks")) {
                setMode(new ManualAlignMode(Display.this));
            } else if (command.equals("Align layers")) {
                Roi roi = canvas.getFakeImagePlus().getRoi();
                if (null != roi) {
                    final YesNoCancelDialog yn = new YesNoCancelDialog(frame, "Use ROI?", "Snapshot layers using the ROI bounds?\n" + roi.getBounds());
                    if (yn.cancelPressed())
                        return;
                    if (!yn.yesPressed()) {
                        roi = null;
                    }
                }
                // caching, since scroll wheel may change it
                final Layer la = layer;
                la.getParent().addTransformStep(la.getParent().getLayers());
                final Bureaucrat burro = AlignLayersTask.alignLayersTask(la, null == roi ? null : roi.getBounds());
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().enlargeToFit(getLayerSet().getDisplayables(Patch.class));
                        la.getParent().addTransformStep(la.getParent().getLayers());
                    }
                });
            } else if (command.equals("Align multi-layer mosaic")) {
                // caching, since scroll wheel may change it
                final Layer la = layer;
                la.getParent().addTransformStep();
                final Bureaucrat burro = AlignTask.alignMultiLayerMosaicTask(la, active instanceof Patch ? (Patch) active : null);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().enlargeToFit(getLayerSet().getDisplayables(Patch.class));
                        la.getParent().addTransformStep();
                    }
                });
            } else if (command.equals("Montage all images in this layer")) {
                final Layer la = layer;
                final List<Patch> patches = new ArrayList<Patch>((List<Patch>) (List) la.getDisplayables(Patch.class, true));
                if (patches.size() < 2) {
                    Utils.showMessage("Montage needs 2 or more visible images");
                    return;
                }
                final Collection<Displayable> col = la.getParent().addTransformStepWithDataForAll(Arrays.asList(new Layer[] { la }));
                // find any locked or selected patches
                final HashSet<Patch> fixed = new HashSet<Patch>();
                for (final Patch p : patches) {
                    if (p.isLocked2() || selection.contains(p))
                        fixed.add(p);
                }
                if (patches.size() == fixed.size()) {
                    Utils.showMessage("Can't do", "No montage possible: all images are selected,\nand hence all are considered locked.\nSelect only one image to be used as reference, or none.");
                    return;
                }
                Utils.log("Using " + fixed.size() + " image" + (fixed.size() == 1 ? "" : "s") + " as reference.");
                final Bureaucrat burro = AlignTask.alignPatchesTask(patches, fixed);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().enlargeToFit(patches);
                        la.getParent().addTransformStepWithData(col);
                    }
                });
            } else if (command.equals("Montage selected images")) {
                final Layer la = layer;
                if (selection.getSelected(Patch.class).size() < 2) {
                    Utils.showMessage("Montage needs 2 or more images selected");
                    return;
                }
                final Collection<Displayable> col = la.getParent().addTransformStepWithDataForAll(Arrays.asList(new Layer[] { la }));
                final Bureaucrat burro = AlignTask.alignSelectionTask(selection);
                if (null == burro)
                    return;
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        la.getParent().enlargeToFit(selection.getAffected());
                        la.getParent().addTransformStepWithData(col);
                    }
                });
            } else if (command.equals("Montage multiple layers")) {
                final GenericDialog gd = new GenericDialog("Choose range");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final List<Layer> layers = getLayerSet().getLayers(gd.getNextChoiceIndex(), gd.getNextChoiceIndex());
                final Collection<Displayable> col = getLayerSet().addTransformStepWithDataForAll(layers);
                final Bureaucrat burro = AlignTask.montageLayersTask(layers);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        final Collection<Displayable> ds = new ArrayList<Displayable>();
                        for (final Layer la : layers) ds.addAll(la.getDisplayables(Patch.class));
                        getLayerSet().enlargeToFit(ds);
                        getLayerSet().addTransformStepWithData(col);
                    }
                });
            } else if (command.equals("Properties ...")) {
                // NOTE the space before the dots, to distinguish from the "Properties..." command that works on Displayable objects.
                adjustProperties();
            } else if (command.equals("Adjust snapping parameters...")) {
                AlignTask.p_snap.setup("Snap");
            } else if (command.equals("Adjust fast-marching parameters...")) {
                Segmentation.fmp.setup();
            } else if (command.equals("Adjust arealist paint parameters...")) {
                AreaWrapper.PP.setup();
            } else if (command.equals("Fill ROI in alpha mask")) {
                if (active.getClass() == Patch.class) {
                    ((Patch) active).keyPressed(new KeyEvent(getCanvas(), -1, System.currentTimeMillis(), 0, KeyEvent.VK_F, 'f'));
                }
            } else if (command.equals("Fill inverse ROI in alpha mask")) {
                if (active.getClass() == Patch.class) {
                    ((Patch) active).keyPressed(new KeyEvent(getCanvas(), -1, System.currentTimeMillis(), Event.SHIFT_MASK, KeyEvent.VK_F, 'f'));
                }
            } else if (command.equals("Search...")) {
                Search.showWindow();
            } else if (command.equals("Select all")) {
                selection.selectAll();
                repaint(Display.this.layer, selection.getBox(), 0);
            } else if (command.equals("Select all visible")) {
                selection.selectAllVisible();
                repaint(Display.this.layer, selection.getBox(), 0);
            } else if (command.equals("Select all that match...")) {
                final List<Displayable> ds = find();
                selection.selectAll(ds);
                Utils.showStatus("Added " + ds.size() + " to selection.");
            } else if (command.equals("Select none")) {
                final Rectangle box = selection.getBox();
                selection.clear();
                repaint(Display.this.layer, box, 0);
            } else if (command.equals("Restore selection")) {
                selection.restore();
            } else if (command.equals("Select under ROI")) {
                final Roi roi = canvas.getFakeImagePlus().getRoi();
                if (null == roi)
                    return;
                selection.selectAll(roi, true);
            } else if (command.equals("Merge") || command.equals("Split")) {
                final Bureaucrat burro = Bureaucrat.create(new Worker.Task(command + "ing AreaLists") {

                    @Override
                    public void exec() {
                        final ArrayList<Displayable> al_sel = selection.getSelected(AreaList.class);
                        // put active at the beginning, to work as the base on which other's will get merged
                        al_sel.remove(Display.this.active);
                        al_sel.add(0, Display.this.active);
                        final Set<DoStep> dataedits = new HashSet<DoStep>();
                        if (command.equals("Merge")) {
                            // Add data undo for active only, which will be edited
                            dataedits.add(new Displayable.DoEdit(Display.this.active).init(Display.this.active, new String[] { "data" }));
                            getLayerSet().addChangeTreesStep(dataedits);
                            final AreaList ali = AreaList.merge(al_sel);
                            if (null != ali) {
                                // remove all but the first from the selection
                                for (int i = 1; i < al_sel.size(); i++) {
                                    final Object ob = al_sel.get(i);
                                    if (ob.getClass() == AreaList.class) {
                                        selection.remove((Displayable) ob);
                                    }
                                }
                                selection.updateTransform(ali);
                                repaint(ali.getLayerSet(), ali, 0);
                            }
                        } else if (command.equals("Split")) {
                            // Add data undo for every AreaList
                            for (final Displayable d : al_sel) {
                                if (d.getClass() != AreaList.class)
                                    continue;
                                dataedits.add(new Displayable.DoEdit(d).init(d, new String[] { "data" }));
                            }
                            getLayerSet().addChangeTreesStep(dataedits);
                            try {
                                List<AreaList> alis = AreaList.split(al_sel);
                                for (AreaList ali : alis) {
                                    if (selection.contains(ali))
                                        continue;
                                    selection.add(ali);
                                }
                            } catch (Exception e) {
                                IJError.print(e);
                                getLayerSet().undoOneStep();
                            }
                        }
                    }
                }, Display.this.project);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        final Set<DoStep> dataedits = new HashSet<DoStep>();
                        dataedits.add(new Displayable.DoEdit(Display.this.active).init(Display.this.active, new String[] { "data" }));
                        getLayerSet().addChangeTreesStep(dataedits);
                    }
                });
                burro.goHaveBreakfast();
            } else if (command.equals("Reroot")) {
                if (!(active instanceof Tree<?>))
                    return;
                getLayerSet().addDataEditStep(active);
                if (((Tree) active).reRoot(((Tree) active).getLastVisited())) {
                    getLayerSet().addDataEditStep(active);
                    Display.repaint(getLayerSet());
                } else {
                    getLayerSet().removeLastUndoStep();
                }
            } else if (command.equals("Part subtree")) {
                if (!(active instanceof Tree<?>))
                    return;
                if (!Utils.check("Really part the subtree?"))
                    return;
                final LayerSet.DoChangeTrees step = getLayerSet().addChangeTreesStep();
                final Set<DoStep> deps = new HashSet<DoStep>();
                // I hate java
                deps.add(new Displayable.DoEdit(active).init(active, new String[] { "data" }));
                step.addDependents(deps);
                final List<ZDisplayable> ts = ((Tree) active).splitAt(((Tree) active).getLastVisited());
                if (null == ts) {
                    getLayerSet().removeLastUndoStep();
                    return;
                }
                final Displayable elder = Display.this.active;
                final HashSet<DoStep> deps2 = new HashSet<DoStep>();
                for (final ZDisplayable t : ts) {
                    deps2.add(new Displayable.DoEdit(t).init(t, new String[] { "data" }));
                    if (t == elder)
                        continue;
                    // will change Display.this.active !
                    getLayerSet().add(t);
                    project.getProjectTree().addSibling(elder, t);
                }
                selection.clear();
                selection.selectAll(ts);
                selection.add(elder);
                final LayerSet.DoChangeTrees step2 = getLayerSet().addChangeTreesStep();
                step2.addDependents(deps2);
                Display.repaint(getLayerSet());
            } else if (command.equals("Show tabular view")) {
                if (!(active instanceof Tree<?>))
                    return;
                ((Tree<?>) active).createMultiTableView();
            } else if (command.equals("Mark")) {
                if (!(active instanceof Tree<?>))
                    return;
                final Point p = canvas.consumeLastPopupPoint();
                if (null == p)
                    return;
                if (((Tree<?>) active).markNear(p.x, p.y, layer, canvas.getMagnification())) {
                    Display.repaint(getLayerSet());
                }
            } else if (command.equals("Clear marks (selected Trees)")) {
                for (final Tree<?> t : selection.get(Tree.class)) {
                    t.unmark();
                }
                Display.repaint(getLayerSet());
            } else if (command.equals("Join")) {
                if (!(active instanceof Tree<?>))
                    return;
                final List<Tree<?>> tlines = (List<Tree<?>>) selection.get(active.getClass());
                if (((Tree) active).canJoin(tlines)) {
                    final int nNodes_active = ((Tree) active).getRoot().getSubtreeNodes().size();
                    String warning = "";
                    for (final Tree<?> t : tlines) {
                        if (active == t)
                            continue;
                        if (null == t.getRoot()) {
                            Utils.log("Removed empty tree #" + t.getId() + " from those to join.");
                            tlines.remove(t);
                            continue;
                        }
                        if (t.getRoot().getSubtreeNodes().size() > nNodes_active) {
                            warning = "\nWARNING joining into a tree that is not the largest!";
                            break;
                        }
                    }
                    if (!Utils.check("Join these " + tlines.size() + " trees into the tree " + active + " ?" + warning))
                        return;
                    // Record current state
                    final Set<DoStep> dataedits = new HashSet<DoStep>(tlines.size());
                    for (final Tree<?> tl : tlines) {
                        dataedits.add(new Displayable.DoEdit(tl).init(tl, new String[] { "data" }));
                    }
                    getLayerSet().addChangeTreesStep(dataedits);
                    // 
                    ((Tree) active).join(tlines);
                    for (final Tree<?> tl : tlines) {
                        if (tl == active)
                            continue;
                        tl.remove2(false);
                    }
                    Display.repaint(getLayerSet());
                    // Again, to record current state (just the joined tree this time)
                    final Set<DoStep> dataedits2 = new HashSet<DoStep>(1);
                    dataedits2.add(new Displayable.DoEdit(active).init(active, new String[] { "data" }));
                    getLayerSet().addChangeTreesStep(dataedits2);
                } else {
                    Utils.showMessage("Can't do", "Only one tree is selected.\nSelect more than one tree to perform a join operation!");
                }
            } else if (command.equals("Previous branch node or start")) {
                if (!(active instanceof Tree<?>))
                    return;
                final Point p = canvas.consumeLastPopupPoint();
                if (null == p)
                    return;
                center(((Treeline) active).findPreviousBranchOrRootPoint(p.x, p.y, layer, canvas));
            } else if (command.equals("Next branch node or end")) {
                if (!(active instanceof Tree<?>))
                    return;
                final Point p = canvas.consumeLastPopupPoint();
                if (null == p)
                    return;
                center(((Tree<?>) active).findNextBranchOrEndPoint(p.x, p.y, layer, canvas));
            } else if (command.equals("Root")) {
                if (!(active instanceof Tree<?>))
                    return;
                final Point p = canvas.consumeLastPopupPoint();
                if (null == p)
                    return;
                center(((Tree) active).createCoordinate(((Tree<?>) active).getRoot()));
            } else if (command.equals("Last added node")) {
                if (!(active instanceof Tree<?>))
                    return;
                center(((Treeline) active).getLastAdded());
            } else if (command.equals("Last edited node")) {
                if (!(active instanceof Tree<?>))
                    return;
                center(((Treeline) active).getLastEdited());
            } else if (command.equals("Reverse point order")) {
                if (!(active instanceof Pipe))
                    return;
                getLayerSet().addDataEditStep(active);
                ((Pipe) active).reverse();
                Display.repaint(Display.this.layer);
                getLayerSet().addDataEditStep(active);
            } else if (command.equals("View orthoslices")) {
                if (!(active instanceof Patch))
                    return;
                Display3D.showOrthoslices(((Patch) active));
            } else if (command.equals("View volume")) {
                if (!(active instanceof Patch))
                    return;
                Display3D.showVolume(((Patch) active));
            } else if (command.equals("Show in 3D")) {
                for (final ZDisplayable zd : selection.get(ZDisplayable.class)) {
                    Display3D.show(zd.getProject().findProjectThing(zd));
                }
                // handle profile lists ...
                final HashSet<ProjectThing> hs = new HashSet<ProjectThing>();
                for (final Profile d : selection.get(Profile.class)) {
                    final ProjectThing profile_list = (ProjectThing) d.getProject().findProjectThing(d).getParent();
                    if (!hs.contains(profile_list)) {
                        Display3D.show(profile_list);
                        hs.add(profile_list);
                    }
                }
            } else if (command.equals("Snap")) {
                // Take the active if it's a Patch
                if (!(active instanceof Patch))
                    return;
                Display.snap((Patch) active);
            } else if (command.equals("Blend") || command.equals("Blend (selected images)...")) {
                final HashSet<Patch> patches = new HashSet<Patch>(selection.get(Patch.class));
                if (patches.size() > 1) {
                    final GenericDialog gd = new GenericDialog("Blending");
                    gd.addCheckbox("Respect current alpha mask", true);
                    gd.showDialog();
                    if (gd.wasCanceled())
                        return;
                    Blending.blend(patches, gd.getNextBoolean());
                } else {
                    IJ.log("Please select more than one overlapping image.");
                }
            } else if (command.equals("Blend (layer-wise)...")) {
                final GenericDialog gd = new GenericDialog("Blending");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.addCheckbox("Respect current alpha mask", true);
                gd.addMessage("Filter:");
                gd.addStringField("Use only images whose title matches:", "", 30);
                gd.addCheckbox("Blend visible patches only", true);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final boolean respect_alpha_mask = gd.getNextBoolean();
                final String toMatch = gd.getNextString().trim();
                final String regex = 0 == toMatch.length() ? null : ".*" + toMatch + ".*";
                final boolean visible_only = gd.getNextBoolean();
                Blending.blendLayerWise(getLayerSet().getLayers(gd.getNextChoiceIndex(), gd.getNextChoiceIndex()), respect_alpha_mask, new Filter<Patch>() {

                    @Override
                    public final boolean accept(final Patch patch) {
                        if (visible_only && !patch.isVisible())
                            return false;
                        if (null == regex)
                            return true;
                        return patch.getTitle().matches(regex);
                    }
                });
            } else if (command.equals("Match intensities (layer-wise)...")) {
                Bureaucrat.createAndStart(new Worker.Task("Match intensities") {

                    @Override
                    public void exec() {
                        final MatchIntensities matching = new MatchIntensities();
                        matching.invoke(getActive());
                    }
                }, project);
            } else if (command.equals("Remove intensity maps (layer-wise)...")) {
                final GenericDialog gd = new GenericDialog("Remove intensity maps");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                Bureaucrat.createAndStart(new Worker.Task("Match intensities") {

                    @Override
                    public void exec() {
                        for (final Layer layer : getLayerSet().getLayers(gd.getNextChoiceIndex(), gd.getNextChoiceIndex())) {
                            for (final Displayable p : layer.getDisplayables(Patch.class)) {
                                final Patch patch = (Patch) p;
                                if (patch.clearIntensityMap()) {
                                    patch.updateMipMaps();
                                }
                            }
                        }
                    }
                }, project);
            } else if (command.equals("Montage")) {
                final Set<Displayable> affected = new HashSet<Displayable>(selection.getAffected());
                // make an undo step!
                final LayerSet ls = layer.getParent();
                ls.addTransformStepWithData(affected);
                final Bureaucrat burro = AlignTask.alignSelectionTask(selection);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        ls.enlargeToFit(affected);
                        ls.addTransformStepWithData(affected);
                    }
                });
            } else if (command.equals("Lens correction")) {
                final Layer la = layer;
                la.getParent().addDataEditStep(new HashSet<Displayable>(la.getParent().getDisplayables()));
                final Bureaucrat burro = DistortionCorrectionTask.correctDistortionFromSelection(selection);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        // no means to know which where modified and from which layers!
                        la.getParent().addDataEditStep(new HashSet<Displayable>(la.getParent().getDisplayables()));
                    }
                });
            } else if (command.equals("Link images...")) {
                final GenericDialog gd = new GenericDialog("Options");
                gd.addMessage("Linking images to images (within their own layer only):");
                final String[] options = { "all images to all images", "each image with any other overlapping image" };
                gd.addChoice("Link: ", options, options[1]);
                final String[] options2 = { "selected images only", "all images in this layer", "all images in all layers, within the layer only", "all images in all layers, within and across consecutive layers" };
                gd.addChoice("Apply to: ", options2, options2[0]);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final Layer lay = layer;
                final HashSet<Displayable> ds = new HashSet<Displayable>(lay.getParent().getDisplayables());
                lay.getParent().addDataEditStep(ds, new String[] { "data" });
                final boolean overlapping_only = 1 == gd.getNextChoiceIndex();
                Collection<Displayable> coll = null;
                switch(gd.getNextChoiceIndex()) {
                    case 0:
                        coll = selection.getSelected(Patch.class);
                        Patch.crosslink(coll, overlapping_only);
                        break;
                    case 1:
                        coll = lay.getDisplayables(Patch.class);
                        Patch.crosslink(coll, overlapping_only);
                        break;
                    case 2:
                        coll = new ArrayList<Displayable>();
                        for (final Layer la : lay.getParent().getLayers()) {
                            final Collection<Displayable> acoll = la.getDisplayables(Patch.class);
                            Patch.crosslink(acoll, overlapping_only);
                            coll.addAll(acoll);
                        }
                        break;
                    case 3:
                        final ArrayList<Layer> layers = lay.getParent().getLayers();
                        Collection<Displayable> lc1 = layers.get(0).getDisplayables(Patch.class);
                        if (lay == layers.get(0))
                            coll = lc1;
                        for (int i = 1; i < layers.size(); i++) {
                            final Collection<Displayable> lc2 = layers.get(i).getDisplayables(Patch.class);
                            if (null == coll && Display.this.layer == layers.get(i))
                                coll = lc2;
                            final Collection<Displayable> both = new ArrayList<Displayable>();
                            both.addAll(lc1);
                            both.addAll(lc2);
                            Patch.crosslink(both, overlapping_only);
                            lc1 = lc2;
                        }
                        break;
                }
                if (null != coll)
                    Display.updateCheckboxes(coll, DisplayablePanel.LINK_STATE, true);
                lay.getParent().addDataEditStep(ds);
            } else if (command.equals("Unlink all selected images")) {
                if (Utils.check("Really unlink selected images?")) {
                    final Collection<Displayable> ds = selection.getSelected(Patch.class);
                    for (final Displayable d : ds) {
                        d.unlink();
                    }
                    Display.updateCheckboxes(ds, DisplayablePanel.LINK_STATE);
                }
            } else if (command.equals("Unlink all")) {
                if (Utils.check("Really unlink all objects from all layers?")) {
                    final Collection<Displayable> ds = layer.getParent().getDisplayables();
                    for (final Displayable d : ds) {
                        d.unlink();
                    }
                    Display.updateCheckboxes(ds, DisplayablePanel.LINK_STATE);
                }
            } else if (command.equals("Calibration...")) {
                try {
                    IJ.run(canvas.getFakeImagePlus(), "Properties...", "");
                    Display.updateTitle(getLayerSet());
                    // repaint layer names
                    project.getLayerTree().updateUILater();
                } catch (final RuntimeException re) {
                    Utils.log2("Calibration dialog canceled.");
                }
            } else if (command.equals("Grid overlay...")) {
                if (null == gridoverlay)
                    gridoverlay = new GridOverlay();
                gridoverlay.setup(canvas.getFakeImagePlus().getRoi());
                canvas.repaint(false);
            } else if (command.equals("Enhance contrast (selected images)...")) {
                final Layer la = layer;
                final ArrayList<Displayable> selected = selection.getSelected(Patch.class);
                final HashSet<Displayable> ds = new HashSet<Displayable>(selected);
                la.getParent().addDataEditStep(ds);
                final Displayable active = Display.this.getActive();
                final Patch ref = active.getClass() == Patch.class ? (Patch) active : null;
                final Bureaucrat burro = getProject().getLoader().enhanceContrast(selected, ref);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        la.getParent().addDataEditStep(ds);
                    }
                });
            } else if (command.equals("Enhance contrast layer-wise...")) {
                // ask for range of layers
                final GenericDialog gd = new GenericDialog("Choose range");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                // exclusive end
                final java.util.List<Layer> layers = layer.getParent().getLayers().subList(gd.getNextChoiceIndex(), gd.getNextChoiceIndex() + 1);
                final HashSet<Displayable> ds = new HashSet<Displayable>();
                for (final Layer l : layers) ds.addAll(l.getDisplayables(Patch.class));
                getLayerSet().addDataEditStep(ds);
                final Bureaucrat burro = project.getLoader().enhanceContrast(layers);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().addDataEditStep(ds);
                    }
                });
            } else if (command.equals("Adjust image filters (selected images)")) {
                if (selection.isEmpty() || !(active instanceof Patch))
                    return;
                FilterEditor.GUI(selection.get(Patch.class), (Patch) active);
            } else if (command.equals("Set Min and Max layer-wise...")) {
                final Displayable active = getActive();
                double min = 0;
                double max = 0;
                if (null != active && active.getClass() == Patch.class) {
                    min = ((Patch) active).getMin();
                    max = ((Patch) active).getMax();
                }
                final GenericDialog gd = new GenericDialog("Min and Max");
                gd.addMessage("Set min and max to all images in the layer range");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.addNumericField("min: ", min, 2);
                gd.addNumericField("max: ", max, 2);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                // 
                min = gd.getNextNumber();
                max = gd.getNextNumber();
                final ArrayList<Displayable> al = new ArrayList<Displayable>();
                for (final Layer la : layer.getParent().getLayers().subList(gd.getNextChoiceIndex(), gd.getNextChoiceIndex() + 1)) {
                    // exclusive end
                    al.addAll(la.getDisplayables(Patch.class));
                }
                final HashSet<Displayable> ds = new HashSet<Displayable>(al);
                getLayerSet().addDataEditStep(ds);
                final Bureaucrat burro = project.getLoader().setMinAndMax(al, min, max);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().addDataEditStep(ds);
                    }
                });
            } else if (command.equals("Set Min and Max (selected images)...")) {
                final Displayable active = getActive();
                double min = 0;
                double max = 0;
                if (null != active && active.getClass() == Patch.class) {
                    min = ((Patch) active).getMin();
                    max = ((Patch) active).getMax();
                }
                final GenericDialog gd = new GenericDialog("Min and Max");
                gd.addMessage("Set min and max to all selected images");
                gd.addNumericField("min: ", min, 2);
                gd.addNumericField("max: ", max, 2);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                // 
                min = gd.getNextNumber();
                max = gd.getNextNumber();
                final HashSet<Displayable> ds = new HashSet<Displayable>(selection.getSelected(Patch.class));
                getLayerSet().addDataEditStep(ds);
                final Bureaucrat burro = project.getLoader().setMinAndMax(selection.getSelected(Patch.class), min, max);
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().addDataEditStep(ds);
                    }
                });
            } else if (command.equals("Adjust min and max (selected images)...")) {
                adjustMinAndMaxGUI();
            } else if (command.equals("Mask image borders (layer-wise)...")) {
                final GenericDialog gd = new GenericDialog("Mask borders");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.addMessage("Borders:");
                gd.addNumericField("left: ", 6, 2);
                gd.addNumericField("top: ", 6, 2);
                gd.addNumericField("right: ", 6, 2);
                gd.addNumericField("bottom: ", 6, 2);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final Collection<Layer> layers = layer.getParent().getLayers().subList(gd.getNextChoiceIndex(), gd.getNextChoiceIndex() + 1);
                final HashSet<Displayable> ds = new HashSet<Displayable>();
                for (final Layer l : layers) ds.addAll(l.getDisplayables(Patch.class));
                getLayerSet().addDataEditStep(ds);
                final Bureaucrat burro = project.getLoader().maskBordersLayerWise(layers, (int) gd.getNextNumber(), (int) gd.getNextNumber(), (int) gd.getNextNumber(), (int) gd.getNextNumber());
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().addDataEditStep(ds);
                    }
                });
            } else if (command.equals("Mask image borders (selected images)...")) {
                final GenericDialog gd = new GenericDialog("Mask borders");
                gd.addMessage("Borders:");
                gd.addNumericField("left: ", 6, 2);
                gd.addNumericField("top: ", 6, 2);
                gd.addNumericField("right: ", 6, 2);
                gd.addNumericField("bottom: ", 6, 2);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final Collection<Displayable> patches = selection.getSelected(Patch.class);
                final HashSet<Displayable> ds = new HashSet<Displayable>(patches);
                getLayerSet().addDataEditStep(ds);
                final Bureaucrat burro = project.getLoader().maskBorders(patches, (int) gd.getNextNumber(), (int) gd.getNextNumber(), (int) gd.getNextNumber(), (int) gd.getNextNumber());
                burro.addPostTask(new Runnable() {

                    @Override
                    public void run() {
                        getLayerSet().addDataEditStep(ds);
                    }
                });
            } else if (command.equals("Remove alpha masks (layer-wise)...")) {
                final GenericDialog gd = new GenericDialog("Remove alpha masks");
                Utils.addLayerRangeChoices(Display.this.layer, gd);
                gd.addCheckbox("Visible only", true);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final Collection<Layer> layers = layer.getParent().getLayers().subList(gd.getNextChoiceIndex(), gd.getNextChoiceIndex() + 1);
                final boolean visible_only = gd.getNextBoolean();
                final Collection<Patch> patches = new ArrayList<Patch>();
                for (final Layer l : layers) {
                    patches.addAll((Collection<Patch>) (Collection) l.getDisplayables(Patch.class, visible_only));
                }
                Display.removeAlphaMasks(patches);
            } else if (command.equals("Remove alpha masks (selected images)...")) {
                Display.removeAlphaMasks(selection.get(Patch.class));
            } else if (command.equals("Split images under polyline ROI")) {
                final Roi roi = canvas.getFakeImagePlus().getRoi();
                if (null == roi)
                    return;
                if (!(roi.getType() == Roi.POLYLINE || roi.getType() == Roi.FREELINE)) {
                    Utils.showMessage("Need a polyline or freeline ROI, not just any ROI!");
                    return;
                }
                if (!Utils.check("Really split images under the ROI?")) {
                    return;
                }
                // OK identify images whose contour intersects the ROI
                final Set<Displayable> col = new HashSet<Displayable>();
                // FreehandRoi is a subclass of PolygonRoi
                final PolygonRoi proi = (PolygonRoi) roi;
                final int[] x = proi.getXCoordinates(), y = proi.getYCoordinates();
                final Rectangle b = proi.getBounds();
                final Polygon[] pols = new Polygon[proi.getNCoordinates() - 1];
                for (int i = 0; i < pols.length; i++) {
                    pols[i] = new Polygon(new int[] { b.x + x[i], b.x + x[i] + 1, b.x + x[i + 1], b.x + x[i + 1] + 1 }, new int[] { b.y + y[i], b.y + y[i], b.y + y[i + 1], b.y + y[i + 1] }, 4);
                }
                for (final Patch p : getLayer().getAll(Patch.class)) {
                    if (!p.isVisible())
                        continue;
                    final Area a = p.getArea();
                    for (int i = 0; i < pols.length; i++) {
                        final Area c = new Area(pols[i]);
                        c.intersect(a);
                        if (M.isEmpty(c))
                            continue;
                        // Else, add it:
                        col.add(p);
                        break;
                    }
                }
                if (col.isEmpty()) {
                    Utils.showMessage("No images intersect the ROI!");
                    return;
                }
                for (int i = 1; i < proi.getNCoordinates(); i++) {
                    for (int k = i + 2; k < proi.getNCoordinates(); k++) {
                        // check if the two segments intersect
                        if (null != M.computeSegmentsIntersection(x[i - 1], y[i - 1], x[i], y[i], x[k - 1], y[k - 1], x[k], y[k])) {
                            Utils.showMessage("Cannot split images with a polygon ROI that intersects itself!");
                            return;
                        }
                    }
                }
                final Area[] as = M.splitArea(new Area(getLayerSet().get2DBounds()), proi, getLayerSet().get2DBounds());
                final Color[] c = new Color[] { Color.blue, Color.red };
                int i = 0;
                for (final Area a : as) {
                    // Utils.log2("Added overlay " + i + " with color " + c[i] + " and area " + AreaCalculations.area(a.getPathIterator(null)));
                    getLayer().getOverlay().add(a, c[i++], null, true, false, 0.4f);
                }
                Display.repaint(getLayer());
                final YesNoDialog yn = new YesNoDialog(frame, "Check", "Does the splitting match your expectations?\nPush 'yes' to split the images.", false);
                yn.setModal(false);
                for (final WindowListener wl : yn.getWindowListeners()) yn.removeWindowListener(wl);
                yn.setClosingTask(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            // Remove overlay shapes
                            for (final Area a : as) {
                                getLayer().getOverlay().remove(a);
                            }
                            if (!yn.yesPressed()) {
                                Utils.log2("Pushed 'no'");
                                return;
                            }
                            // Split intersecting patches
                            // Duplicate each intersecting patch, and assign a[0] to the original and a[1] to the copy, as mask.
                            Bureaucrat.createAndStart(new Worker.Task("Spliting images") {

                                @Override
                                public void exec() {
                                    final Roi r1 = new ShapeRoi(as[0]), r2 = new ShapeRoi(as[1]);
                                    final ArrayList<Future<?>> fus = new ArrayList<Future<?>>();
                                    for (final Patch p : (Collection<Patch>) (Collection) col) {
                                        final Patch copy = (Patch) p.clone(p.getProject(), false);
                                        p.addAlphaMask(r1, 0);
                                        copy.addAlphaMask(r2, 0);
                                        fus.add(p.updateMipMaps());
                                        fus.add(copy.updateMipMaps());
                                        // after submitting mipmaps, since it will get added to all Displays and repainted.
                                        p.getLayer().add(copy);
                                    }
                                    Utils.wait(fus);
                                }
                            }, project);
                        } catch (final Throwable t) {
                            IJError.print(t);
                        } finally {
                            yn.dispose();
                            Display.repaint(getLayer());
                        }
                    }
                });
                yn.setVisible(true);
            } else if (command.equals("Duplicate")) {
                // only Patch and DLabel, i.e. Layer-only resident objects that don't exist in the Project Tree
                final HashSet<Class> accepted = new HashSet<Class>();
                accepted.add(Patch.class);
                accepted.add(DLabel.class);
                accepted.add(Stack.class);
                final ArrayList<Displayable> originals = new ArrayList<Displayable>();
                final ArrayList<Displayable> selected = selection.getSelected();
                for (final Displayable d : selected) {
                    if (accepted.contains(d.getClass())) {
                        originals.add(d);
                    }
                }
                if (originals.size() > 0) {
                    getLayerSet().addChangeTreesStep();
                    for (final Displayable d : originals) {
                        if (d instanceof ZDisplayable) {
                            d.getLayerSet().add((ZDisplayable) d.clone());
                        } else {
                            d.getLayer().add(d.clone());
                        }
                    }
                    getLayerSet().addChangeTreesStep();
                } else if (selected.size() > 0) {
                    Utils.log("Can only duplicate images and text labels.\nDuplicate *other* objects in the Project Tree.\n");
                }
            } else if (command.equals("Create subproject")) {
                // Choose a 2D rectangle
                final Roi roi = canvas.getFakeImagePlus().getRoi();
                Rectangle bounds;
                if (null != roi) {
                    if (!Utils.check("Use bounds as defined by the ROI:\n" + roi.getBounds() + " ?"))
                        return;
                    bounds = roi.getBounds();
                } else
                    bounds = getLayerSet().get2DBounds();
                // Choose a layer range, and whether to ignore hidden images
                final GenericDialog gd = new GenericDialog("Choose layer range");
                Utils.addLayerRangeChoices(layer, gd);
                gd.addCheckbox("Ignore hidden images", true);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final Layer first = layer.getParent().getLayer(gd.getNextChoiceIndex());
                final Layer last = layer.getParent().getLayer(gd.getNextChoiceIndex());
                final boolean ignore_hidden_patches = gd.getNextBoolean();
                final Project sub = getProject().createSubproject(bounds, first, last, ignore_hidden_patches);
                if (null == sub) {
                    Utils.log("ERROR: failed to create subproject.");
                    return;
                }
                final LayerSet subls = sub.getRootLayerSet();
                Display.createDisplay(sub, subls.getLayer(0));
            } else if (command.startsWith("Image stack under selected Arealist")) {
                if (null == active || active.getClass() != AreaList.class)
                    return;
                final GenericDialog gd = new GenericDialog("Stack options");
                final String[] types = { "8-bit", "16-bit", "32-bit", "RGB" };
                gd.addChoice("type:", types, types[0]);
                gd.addSlider("Scale: ", 1, 100, 100);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final int type;
                switch(gd.getNextChoiceIndex()) {
                    case 0:
                        type = ImagePlus.GRAY8;
                        break;
                    case 1:
                        type = ImagePlus.GRAY16;
                        break;
                    case 2:
                        type = ImagePlus.GRAY32;
                        break;
                    case 3:
                        type = ImagePlus.COLOR_RGB;
                        break;
                    default:
                        type = ImagePlus.GRAY8;
                        break;
                }
                final ImagePlus imp = ((AreaList) active).getStack(type, gd.getNextNumber() / 100);
                if (null != imp)
                    imp.show();
            } else if (command.equals("Fly through selected Treeline/AreaTree")) {
                if (null == active || !(active instanceof Tree<?>))
                    return;
                Bureaucrat.createAndStart(new Worker.Task("Creating fly through", true) {

                    @Override
                    public void exec() {
                        final GenericDialog gd = new GenericDialog("Fly through");
                        gd.addNumericField("Width", 512, 0);
                        gd.addNumericField("Height", 512, 0);
                        final String[] types = new String[] { "8-bit gray", "Color RGB" };
                        gd.addChoice("Image type", types, types[0]);
                        gd.addSlider("scale", 0, 100, 100);
                        gd.addCheckbox("save to file", false);
                        gd.showDialog();
                        if (gd.wasCanceled())
                            return;
                        final int w = (int) gd.getNextNumber();
                        final int h = (int) gd.getNextNumber();
                        final int type = 0 == gd.getNextChoiceIndex() ? ImagePlus.GRAY8 : ImagePlus.COLOR_RGB;
                        final double scale = gd.getNextNumber();
                        if (w <= 0 || h <= 0) {
                            Utils.log("Invalid width or height: " + w + ", " + h);
                            return;
                        }
                        if (0 == scale || Double.isNaN(scale)) {
                            Utils.log("Invalid scale: " + scale);
                            return;
                        }
                        String dir = null;
                        if (gd.getNextBoolean()) {
                            final DirectoryChooser dc = new DirectoryChooser("Target directory");
                            dir = dc.getDirectory();
                            // canceled
                            if (null == dir)
                                return;
                            dir = Utils.fixDir(dir);
                        }
                        final ImagePlus imp = ((Tree<?>) active).flyThroughMarked(w, h, scale / 100, type, dir);
                        if (null == imp) {
                            Utils.log("Mark a node first!");
                            return;
                        }
                        imp.show();
                    }
                }, project);
            } else if (command.startsWith("Arealists as labels")) {
                final GenericDialog gd = new GenericDialog("Export labels");
                gd.addSlider("Scale: ", 1, 100, 100);
                final String[] options = { "All area list", "Selected area lists" };
                gd.addChoice("Export: ", options, options[0]);
                Utils.addLayerRangeChoices(layer, gd);
                gd.addCheckbox("Visible only", true);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final float scale = (float) (gd.getNextNumber() / 100);
                final java.util.List<Displayable> al = (java.util.List<Displayable>) (0 == gd.getNextChoiceIndex() ? layer.getParent().getZDisplayables(AreaList.class) : selection.getSelectedSorted(AreaList.class));
                if (null == al) {
                    Utils.log("No area lists found to export.");
                    return;
                }
                // Generics are ... a pain? I don't understand them? They fail when they shouldn't? And so easy to workaround that they are a shame?
                final int first = gd.getNextChoiceIndex();
                final int last = gd.getNextChoiceIndex();
                final boolean visible_only = gd.getNextBoolean();
                if (-1 != command.indexOf("(amira)")) {
                    AreaList.exportAsLabels(al, canvas.getFakeImagePlus().getRoi(), scale, first, last, visible_only, true, true);
                } else if (-1 != command.indexOf("(tif)")) {
                    AreaList.exportAsLabels(al, canvas.getFakeImagePlus().getRoi(), scale, first, last, visible_only, false, false);
                }
            } else if (command.equals("Project properties...")) {
                project.adjustProperties();
            } else if (command.equals("Release memory...")) {
                Bureaucrat.createAndStart(new Worker("Releasing memory") {

                    @Override
                    public void run() {
                        startedWorking();
                        try {
                            final GenericDialog gd = new GenericDialog("Release Memory");
                            final int max = (int) (IJ.maxMemory() / 1000000);
                            gd.addSlider("Megabytes: ", 0, max, max / 2);
                            gd.showDialog();
                            if (!gd.wasCanceled()) {
                                final int n_mb = (int) gd.getNextNumber();
                                project.getLoader().releaseToFit((long) n_mb * 1000000);
                            }
                        } catch (final Throwable e) {
                            IJError.print(e);
                        } finally {
                            finishedWorking();
                        }
                    }
                }, project);
            } else if (command.equals("Create sibling project with retiled layers")) {
                final GenericDialog gd = new GenericDialog("Export flattened layers");
                gd.addNumericField("Tile_width", 2048, 0);
                gd.addNumericField("Tile_height", 2048, 0);
                final String[] types = new String[] { "16-bit", "RGB color" };
                gd.addChoice("Export_image_type", types, types[0]);
                gd.addCheckbox("Create mipmaps", true);
                gd.addNumericField("Number_of_threads_to_use", Runtime.getRuntime().availableProcessors(), 0);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final DirectoryChooser dc = new DirectoryChooser("Choose target folder");
                final String folder = dc.getDirectory();
                if (null == folder)
                    return;
                final int tileWidth = (int) gd.getNextNumber(), tileHeight = (int) gd.getNextNumber();
                if (tileWidth < 0 || tileHeight < 0) {
                    Utils.showMessage("Invalid tile sizes: " + tileWidth + ", " + tileHeight);
                    return;
                }
                if (tileWidth != tileHeight) {
                    if (!Utils.check("The tile width (" + tileWidth + ") differs from the tile height (" + tileHeight + ").\nContinue anyway?")) {
                        return;
                    }
                }
                final int imageType = 0 == gd.getNextChoiceIndex() ? ImagePlus.GRAY16 : ImagePlus.COLOR_RGB;
                final boolean createMipMaps = gd.getNextBoolean();
                final int nThreads = (int) gd.getNextNumber();
                Bureaucrat.createAndStart(new Worker.Task("Export flattened sibling project") {

                    @Override
                    public void exec() {
                        try {
                            ProjectTiler.createRetiledSibling(project, folder, tileWidth, tileHeight, imageType, true, nThreads, createMipMaps);
                        } catch (final Throwable t) {
                            Utils.showMessage("ERROR: " + t);
                            IJError.print(t);
                        }
                    }
                }, project);
            } else if (command.equals("Flush image cache")) {
                Loader.releaseAllCaches();
            } else if (command.equals("Regenerate all mipmaps")) {
                project.getLoader().regenerateMipMaps(getLayerSet().getDisplayables(Patch.class));
            } else if (command.equals("Regenerate mipmaps (selected images)")) {
                project.getLoader().regenerateMipMaps(selection.getSelected(Patch.class));
            } else if (command.equals("Tags...")) {
                // get a file first
                final File f = Utils.chooseFile(null, "tags", ".xml");
                if (null == f)
                    return;
                if (!Utils.saveToFile(f, getLayerSet().exportTags())) {
                    Utils.logAll("ERROR when saving tags to file " + f.getAbsolutePath());
                }
            } else if (command.equals("Tags ...")) {
                final String[] ff = Utils.selectFile("Import tags");
                if (null == ff)
                    return;
                final GenericDialog gd = new GenericDialog("Import tags");
                final String[] modes = new String[] { "Append to current tags", "Replace current tags" };
                gd.addChoice("Import tags mode:", modes, modes[0]);
                gd.addMessage("Replacing current tags\nwill remove all tags\n from all nodes first!");
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                getLayerSet().importTags(new StringBuilder(ff[0]).append('/').append(ff[1]).toString(), 1 == gd.getNextChoiceIndex());
            } else if (command.equals("Connectivity graph...")) {
                Bureaucrat.createAndStart(new Worker.Task("Connectivity graph") {

                    @Override
                    public void exec() {
                        Graph.extractAndShowGraph(getLayerSet());
                    }
                }, getProject());
            } else if (command.equals("NeuroML...")) {
                final GenericDialog gd = new GenericDialog("Export NeuroML");
                final String[] a = new String[] { "NeuroML (arbors and synapses)", "MorphML (arbors)" };
                gd.addChoice("Type:", a, a[0]);
                final String[] b = new String[] { "All treelines and areatrees", "Selected treelines and areatrees" };
                gd.addChoice("Export:", b, b[0]);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                final int type = gd.getNextChoiceIndex();
                final int export = gd.getNextChoiceIndex();
                // 
                final SaveDialog sd = new SaveDialog("Choose .mml file", null, ".mml");
                final String filename = sd.getFileName();
                // canceled
                if (null == filename)
                    return;
                final File f = new File(sd.getDirectory() + filename);
                // 
                Bureaucrat.createAndStart(new Worker.Task("Export NeuroML") {

                    @Override
                    public void exec() {
                        OutputStreamWriter w = null;
                        try {
                            final Set<Tree<?>> trees = new HashSet<Tree<?>>();
                            Collection<? extends Displayable> ds = null;
                            switch(export) {
                                case 0:
                                    ds = getLayerSet().getZDisplayables();
                                    break;
                                case 1:
                                    ds = selection.getSelected();
                                    break;
                            }
                            for (final Displayable d : ds) {
                                if (d.getClass() == Treeline.class || d.getClass() == AreaTree.class) {
                                    trees.add((Tree<?>) d);
                                }
                            }
                            if (trees.isEmpty()) {
                                Utils.showMessage("No trees to export!");
                                return;
                            }
                            // 
                            // encoding in Latin 1 (for macosx not to mess around
                            w = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(f), 65536), "8859_1");
                            // 
                            switch(type) {
                                case 0:
                                    NeuroML.exportNeuroML(trees, w);
                                    break;
                                case 1:
                                    NeuroML.exportMorphML(trees, w);
                                    break;
                            }
                            // 
                            w.flush();
                            w.close();
                        // 
                        } catch (final Throwable t) {
                            IJError.print(t);
                            try {
                                if (null != w)
                                    w.close();
                            } catch (final Exception ee) {
                                IJError.print(ee);
                            }
                        }
                    }
                }, getProject());
            } else if (command.equals("Measure")) {
                if (selection.isEmpty()) {
                    Utils.log("Nothing selected to measure!");
                    return;
                }
                selection.measure();
            } else if (command.equals("Area interpolation options...")) {
                final GenericDialog gd = new GenericDialog("Area interpolation");
                final boolean a = project.getBooleanProperty(AreaUtils.always_interpolate_areas_with_distance_map);
                gd.addCheckbox("Always use distance map method", a);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                project.setProperty(AreaUtils.always_interpolate_areas_with_distance_map, gd.getNextBoolean() ? "true" : null);
            } else {
                Utils.log2("Display: don't know what to do with command " + command);
            }
        }
    });
}
Also used : ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) Bureaucrat(ini.trakem2.utils.Bureaucrat) MatchIntensities(org.janelia.intensity.MatchIntensities) SaveDialog(ij.io.SaveDialog) Vector(java.util.Vector) TextListener(java.awt.event.TextListener) Project(ini.trakem2.Project) DBObject(ini.trakem2.persistence.DBObject) ItemEvent(java.awt.event.ItemEvent) Set(java.util.Set) HashSet(java.util.HashSet) GenericDialog(ij.gui.GenericDialog) InspectPatchTrianglesMode(ini.trakem2.display.inspect.InspectPatchTrianglesMode) Worker(ini.trakem2.utils.Worker) ProjectThing(ini.trakem2.tree.ProjectThing) TextEvent(java.awt.event.TextEvent) WindowListener(java.awt.event.WindowListener) Hashtable(java.util.Hashtable) ImagePlus(ij.ImagePlus) ItemListener(java.awt.event.ItemListener) OutputStreamWriter(java.io.OutputStreamWriter) DirectoryChooser(ij.io.DirectoryChooser) AlignTask(mpicbg.trakem2.align.AlignTask) AlignLayersTask(mpicbg.trakem2.align.AlignLayersTask) DistortionCorrectionTask(lenscorrection.DistortionCorrectionTask) Choice(java.awt.Choice) LayerStack(ini.trakem2.imaging.LayerStack) TextField(java.awt.TextField) ArrayList(java.util.ArrayList) List(java.util.List) CoordinateTransformList(mpicbg.trakem2.transform.CoordinateTransformList) Polygon(java.awt.Polygon) HashSet(java.util.HashSet) Color(java.awt.Color) PolygonRoi(ij.gui.PolygonRoi) ShapeRoi(ij.gui.ShapeRoi) Roi(ij.gui.Roi) Area(java.awt.geom.Area) FileOutputStream(java.io.FileOutputStream) Collection(java.util.Collection) File(java.io.File) Saver(ini.trakem2.utils.Saver) KeyEvent(java.awt.event.KeyEvent) PolygonRoi(ij.gui.PolygonRoi) ListIterator(java.util.ListIterator) Iterator(java.util.Iterator) YesNoCancelDialog(ij.gui.YesNoCancelDialog) BufferedOutputStream(java.io.BufferedOutputStream) ShapeRoi(ij.gui.ShapeRoi) Point(java.awt.Point) Point(java.awt.Point) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Filter(ini.trakem2.utils.Filter) Future(java.util.concurrent.Future)

Example 32 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Display method addAreaListAreasMenu.

private void addAreaListAreasMenu(final JPopupMenu popup2, final Displayable active) {
    final ActionListener listener = new ActionListener() {

        @Override
        public void actionPerformed(final ActionEvent ae) {
            final String command = ae.getActionCommand();
            Bureaucrat.createAndStart(new Worker.Task(command) {

                @Override
                public void exec() {
                    if (command.equals("Copy area")) {
                        if (null == active || !(active instanceof AreaList))
                            return;
                        final AreaList ali = (AreaList) active;
                        final Area area = ali.getArea(getLayer());
                        if (null == area)
                            return;
                        DisplayCanvas.setCopyBuffer(ali.getClass(), area.createTransformedArea(ali.getAffineTransform()));
                    } else if (command.equals("Paste area")) {
                        if (null == active || !(active instanceof AreaList))
                            return;
                        final AreaList ali = (AreaList) active;
                        final Area wa = (Area) DisplayCanvas.getCopyBuffer(ali.getClass());
                        if (null == wa)
                            return;
                        try {
                            getLayerSet().addDataEditStep(ali);
                            ali.addArea(getLayer().getId(), wa.createTransformedArea(ali.getAffineTransform().createInverse()));
                            ali.calculateBoundingBox(getLayer());
                            getLayerSet().addDataEditStep(ali);
                        } catch (final NoninvertibleTransformException e) {
                            IJError.print(e);
                            getLayerSet().undoOneStep();
                        }
                    } else if (command.equals("Interpolate gaps towards previous area")) {
                        if (null == active || !(active instanceof AreaList))
                            return;
                        final AreaList ali = (AreaList) active;
                        // Is there an area in this layer?
                        final Layer current = getLayer();
                        if (null == ali.getArea(current))
                            return;
                        // Find a layer before the current that has an area
                        final LayerSet ls = getLayerSet();
                        // already at first
                        if (0 == ls.indexOf(current))
                            return;
                        Layer previous = null;
                        // Iterate layers towards the first layer
                        for (final ListIterator<Layer> it = ls.getLayers().listIterator(ls.indexOf(current)); it.hasPrevious(); ) {
                            final Layer la = it.previous();
                            if (null != ali.getArea(la)) {
                                previous = la;
                                break;
                            }
                        }
                        // all empty
                        if (null == previous)
                            return;
                        try {
                            ls.addDataEditStep(ali);
                            ali.interpolate(previous, current, project.getBooleanProperty(AreaUtils.always_interpolate_areas_with_distance_map));
                            ls.addDataEditStep(ali);
                        } catch (final Exception e) {
                            IJError.print(e);
                            ls.undoOneStep();
                        }
                    } else if (command.equals("Interpolate gaps towards next area")) {
                        if (null == active || !(active instanceof AreaList))
                            return;
                        final AreaList ali = (AreaList) active;
                        // Is there an area in this layer?
                        final Layer current = getLayer();
                        if (null == ali.getArea(current))
                            return;
                        // Find a layer after the current that has an area
                        final LayerSet ls = getLayerSet();
                        // already at the end
                        if (ls.size() - 1 == ls.indexOf(current))
                            return;
                        Layer next = null;
                        // Iterate towards the next layer
                        for (final ListIterator<Layer> it = ls.getLayers().listIterator(ls.indexOf(current) + 1); it.hasNext(); ) {
                            final Layer la = it.next();
                            if (null != ali.getArea(la)) {
                                next = la;
                                break;
                            }
                        }
                        // all empty
                        if (null == next)
                            return;
                        try {
                            ls.addDataEditStep(ali);
                            ali.interpolate(current, next, project.getBooleanProperty(AreaUtils.always_interpolate_areas_with_distance_map));
                            ls.addDataEditStep(ali);
                        } catch (final Exception e) {
                            IJError.print(e);
                            ls.undoOneStep();
                        }
                    } else if (command.equals("Interpolate all gaps")) {
                        if (null == active || !(active instanceof AreaList))
                            return;
                        final AreaList ali = (AreaList) active;
                        // find the first and last layers with areas
                        Layer first = null;
                        Layer last = null;
                        final LayerSet ls = getLayerSet();
                        final List<Layer> las = ls.getLayers();
                        for (final Layer la : las) {
                            if (null == first && null != ali.getArea(la)) {
                                first = la;
                                break;
                            }
                        }
                        for (final ListIterator<Layer> it = las.listIterator(las.size()); it.hasPrevious(); ) {
                            final Layer la = it.previous();
                            if (null == last && null != ali.getArea(la)) {
                                last = la;
                                break;
                            }
                        }
                        Utils.log2(first, last);
                        if (null != first && first != last) {
                            try {
                                ls.addDataEditStep(ali);
                                ali.interpolate(first, last, project.getBooleanProperty(AreaUtils.always_interpolate_areas_with_distance_map));
                                ls.addDataEditStep(ali);
                            } catch (final Exception e) {
                                IJError.print(e);
                                ls.undoOneStep();
                            }
                        }
                    }
                    Display.repaint(getLayer());
                }
            }, active.getProject());
        }
    };
    final JMenu interpolate = new JMenu("Areas");
    JMenuItem item = new JMenuItem("Interpolate gaps towards previous area");
    item.addActionListener(listener);
    interpolate.add(item);
    item = new JMenuItem("Interpolate gaps towards next area");
    item.addActionListener(listener);
    interpolate.add(item);
    item = new JMenuItem("Interpolate all gaps");
    item.addActionListener(listener);
    interpolate.add(item);
    item = new JMenuItem("Area interpolation options...");
    item.addActionListener(Display.this);
    interpolate.add(item);
    interpolate.addSeparator();
    item = new JMenuItem("Copy area");
    item.addActionListener(listener);
    interpolate.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0, true));
    item = new JMenuItem("Paste area");
    item.addActionListener(listener);
    interpolate.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, 0, true));
    item.setEnabled(null != DisplayCanvas.getCopyBuffer(active.getClass()));
    popup.add(interpolate);
}
Also used : ActionEvent(java.awt.event.ActionEvent) ListIterator(java.util.ListIterator) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Area(java.awt.geom.Area) ActionListener(java.awt.event.ActionListener) Worker(ini.trakem2.utils.Worker) ArrayList(java.util.ArrayList) List(java.util.List) CoordinateTransformList(mpicbg.trakem2.transform.CoordinateTransformList) JMenuItem(javax.swing.JMenuItem) JMenu(javax.swing.JMenu)

Example 33 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Display method addAreaTreeAreasMenu.

private void addAreaTreeAreasMenu(final JPopupMenu popup, final AreaTree atree) {
    final ActionListener listener = new ActionListener() {

        private final Node<?> findNearestNode() {
            final Layer la = getLayer();
            final Point p = canvas.consumeLastPopupPoint();
            final Node<?> lv = atree.getLastVisited();
            boolean use_last_visited = false;
            if (null != lv) {
                final float[] xy = new float[] { lv.x, lv.y };
                atree.getAffineTransform().transform(xy, 0, xy, 0, 1);
                use_last_visited = lv.getLayer() == la && canvas.getSrcRect().contains((int) xy[0], (int) xy[1]);
            }
            // if no node lays near the clicked point.
            return atree.findNodeNear(p.x, p.y, la, canvas, use_last_visited);
        }

        @Override
        public void actionPerformed(final ActionEvent ae) {
            final String command = ae.getActionCommand();
            final LayerSet ls = atree.getLayerSet();
            Bureaucrat.createAndStart(new Worker.Task(command) {

                @Override
                public void exec() {
                    final Node<?> nd = findNearestNode();
                    if (null == nd) {
                        Utils.log("No node found in the field of view!");
                        return;
                    }
                    if (command.equals("Copy area")) {
                        final Area area = (Area) nd.getData();
                        if (null == area)
                            return;
                        DisplayCanvas.setCopyBuffer(atree.getClass(), area.createTransformedArea(atree.getAffineTransform()));
                    } else if (command.equals("Paste area")) {
                        final Area wa = (Area) DisplayCanvas.getCopyBuffer(atree.getClass());
                        if (null == wa)
                            return;
                        try {
                            getLayerSet().addDataEditStep(atree);
                            atree.addWorldAreaTo(nd, wa);
                            atree.calculateBoundingBox(nd.getLayer());
                            getLayerSet().addDataEditStep(atree);
                        } catch (final Exception e) {
                            IJError.print(e);
                            getLayerSet().removeLastUndoStep();
                        }
                    } else if (command.equals("Interpolate gaps towards parent (node-centric)")) {
                        interpolate(nd, true);
                    } else if (command.equals("Interpolate gaps towards parent (absolute)")) {
                        interpolate(nd, false);
                    } else if (command.equals("Interpolate all gaps")) {
                        final GenericDialog gd = new GenericDialog("Interpolate");
                        final String[] a = new String[] { "node-centric", "absolute" };
                        gd.addChoice("Mode", a, a[0]);
                        gd.addCheckbox("Always use distance map", project.getBooleanProperty(AreaUtils.always_interpolate_areas_with_distance_map));
                        final String[] b = new String[] { "All selected AreaTrees", "Active AreaTree" };
                        gd.addChoice("Process", b, b[0]);
                        gd.showDialog();
                        if (gd.wasCanceled())
                            return;
                        final boolean node_centric = 0 == gd.getNextChoiceIndex();
                        final boolean use_distance_map = gd.getNextBoolean();
                        final boolean all = 0 == gd.getNextChoiceIndex();
                        final Set<Displayable> s = new HashSet<Displayable>();
                        if (all)
                            s.addAll(selection.get(AreaTree.class));
                        else
                            s.add(atree);
                        // Store current state for undo
                        ls.addDataEditStep(s);
                        try {
                            for (final Displayable d : s) {
                                ((AreaTree) d).interpolateAllGaps(node_centric, use_distance_map);
                            }
                            ls.addDataEditStep(s);
                        } catch (final Exception e) {
                            IJError.print(e);
                            ls.undoOneStep();
                        }
                        Display.repaint();
                    }
                }

                private final void interpolate(final Node<?> nd, final boolean node_centric) {
                    if (null == nd.getDataCopy() || ((Area) nd.getData()).isEmpty()) {
                        Utils.log("Can't interpolate: node lacks an area!");
                        return;
                    }
                    ls.addDataEditStep(atree);
                    try {
                        if (atree.interpolateTowardsParent((AreaTree.AreaNode) nd, node_centric, project.getBooleanProperty(AreaUtils.always_interpolate_areas_with_distance_map))) {
                            ls.addDataEditStep(atree);
                        } else {
                            Utils.log("Nothing to interpolate: the parent node already has an area.");
                            ls.removeLastUndoStep();
                        }
                    } catch (final Exception e) {
                        IJError.print(e);
                        ls.undoOneStep();
                    }
                    Display.repaint();
                }
            }, atree.getProject());
        }
    };
    final JMenu interpolate = new JMenu("Areas");
    JMenuItem item = new JMenuItem("Interpolate gaps towards parent (node-centric)");
    item.addActionListener(listener);
    interpolate.add(item);
    item = new JMenuItem("Interpolate gaps towards parent (absolute)");
    item.addActionListener(listener);
    interpolate.add(item);
    item = new JMenuItem("Interpolate all gaps");
    item.addActionListener(listener);
    interpolate.add(item);
    item = new JMenuItem("Area interpolation options...");
    item.addActionListener(Display.this);
    interpolate.add(item);
    interpolate.addSeparator();
    item = new JMenuItem("Copy area");
    item.addActionListener(listener);
    interpolate.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0, true));
    item = new JMenuItem("Paste area");
    item.addActionListener(listener);
    interpolate.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, 0, true));
    item.setEnabled(null != DisplayCanvas.getCopyBuffer(active.getClass()));
    popup.add(interpolate);
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) ActionEvent(java.awt.event.ActionEvent) Point(java.awt.Point) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Area(java.awt.geom.Area) ActionListener(java.awt.event.ActionListener) GenericDialog(ij.gui.GenericDialog) Worker(ini.trakem2.utils.Worker) JMenuItem(javax.swing.JMenuItem) JMenu(javax.swing.JMenu)

Example 34 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Display method insertStack.

/**
 * @param stack_patch is just a Patch of a series of Patch that make a stack of Patches.
 */
private boolean insertStack(final ProjectThing target_landmarks, final Project source, final ProjectThing source_landmarks, final Patch stack_patch) {
    final List<Ball> l1 = new ArrayList<Ball>();
    final List<Ball> l2 = new ArrayList<Ball>();
    // source is the one that has the stack_patch
    final Collection<ProjectThing> b1s = source_landmarks.findChildrenOfType("ball");
    // target is this
    final Collection<ProjectThing> b2s = target_landmarks.findChildrenOfType("ball");
    final HashSet<String> seen = new HashSet<String>();
    for (final ProjectThing b1 : b1s) {
        final Ball ball1 = (Ball) b1.getObject();
        if (null == ball1) {
            Utils.log("ERROR: there's an empty 'ball' node in target project" + project.toString());
            return false;
        }
        final String title1 = ball1.getTitle();
        for (final ProjectThing b2 : b2s) {
            final Ball ball2 = (Ball) b2.getObject();
            if (null == ball2) {
                Utils.log("ERROR: there's an empty 'ball' node in source project" + source.toString());
                return false;
            }
            if (title1.equals(ball2.getTitle())) {
                if (seen.contains(title1))
                    continue;
                seen.add(title1);
                l1.add(ball1);
                l2.add(ball2);
            }
        }
    }
    if (l1.size() < 4) {
        Utils.log("ERROR: found only " + l1.size() + " common landmarks: needs at least 4!");
        return false;
    }
    // Extract coordinates of source project landmarks, in patch stack coordinate space
    final List<double[]> c1 = new ArrayList<double[]>();
    for (final Ball ball1 : l1) {
        final Map<Layer, double[]> m = ball1.getRawBalls();
        if (1 != m.size()) {
            Utils.log("ERROR: ball object " + ball1 + " from target project " + project + " has " + m.size() + " balls instead of just 1.");
            return false;
        }
        final Map.Entry<Layer, double[]> e = m.entrySet().iterator().next();
        final Layer layer = e.getKey();
        final double[] xyr = e.getValue();
        final double[] fin = new double[] { xyr[0], xyr[1] };
        final AffineTransform affine = ball1.getAffineTransformCopy();
        try {
            affine.preConcatenate(stack_patch.getAffineTransform().createInverse());
        } catch (final Exception nite) {
            IJError.print(nite);
            return false;
        }
        final double[] fout = new double[2];
        affine.transform(fin, 0, fout, 0, 1);
        c1.add(new double[] { fout[0], fout[1], layer.getParent().indexOf(layer) });
    }
    // Extract coordinates of target (this) project landmarks, in calibrated world space
    final List<double[]> c2 = new ArrayList<double[]>();
    for (final Ball ball2 : l2) {
        final double[][] b = ball2.getBalls();
        if (1 != b.length) {
            Utils.log("ERROR: ball object " + ball2 + " from source project " + source + " has " + b.length + " balls instead of just 1.");
            return false;
        }
        final double[] fin = new double[] { b[0][0], b[0][1] };
        final AffineTransform affine = ball2.getAffineTransformCopy();
        final double[] fout = new double[2];
        affine.transform(fin, 0, fout, 0, 1);
        c2.add(new double[] { fout[0], fout[1], b[0][2] });
    }
    // Print landmarks:
    Utils.log("Landmarks:");
    for (Iterator<double[]> it1 = c1.iterator(), it2 = c2.iterator(); it1.hasNext(); ) {
        Utils.log(Utils.toString(it1.next()) + " <--> " + Utils.toString(it2.next()));
    }
    // Create point matches
    final List<PointMatch> pm = new ArrayList<PointMatch>();
    for (Iterator<double[]> it1 = c1.iterator(), it2 = c2.iterator(); it1.hasNext(); ) {
        pm.add(new mpicbg.models.PointMatch(new mpicbg.models.Point(it1.next()), new mpicbg.models.Point(it2.next())));
    }
    // Estimate AffineModel3D
    final AffineModel3D aff3d = new AffineModel3D();
    try {
        aff3d.fit(pm);
    } catch (final Exception e) {
        IJError.print(e);
        return false;
    }
    // Create and add the Stack
    final String path = stack_patch.getImageFilePath();
    final Stack st = new Stack(project, new File(path).getName(), 0, 0, getLayerSet().getLayers().get(0), path);
    st.setInvertibleCoordinateTransform(aff3d);
    getLayerSet().add(st);
    return true;
}
Also used : PointMatch(mpicbg.models.PointMatch) ArrayList(java.util.ArrayList) ProjectThing(ini.trakem2.tree.ProjectThing) HashSet(java.util.HashSet) Point(java.awt.Point) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) LayerStack(ini.trakem2.imaging.LayerStack) PatchStack(ini.trakem2.imaging.PatchStack) PointMatch(mpicbg.models.PointMatch) AffineTransform(java.awt.geom.AffineTransform) Map(java.util.Map) TreeMap(java.util.TreeMap) HashMap(java.util.HashMap) File(java.io.File) AffineModel3D(mpicbg.trakem2.transform.AffineModel3D)

Example 35 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Display method makeGUI.

private void makeGUI(final Layer layer, final Object[] props) {
    // gather properties
    final Point p;
    double mag = 1.0D;
    Rectangle srcRect = null;
    if (null != props) {
        p = (Point) props[0];
        mag = ((Double) props[1]).doubleValue();
        srcRect = (Rectangle) props[2];
    } else {
        p = null;
    }
    // transparency slider
    this.transp_slider = new JSlider(javax.swing.SwingConstants.HORIZONTAL, 0, 100, 100);
    this.transp_slider.setBackground(Color.white);
    this.transp_slider.setMinimumSize(new Dimension(250, 20));
    this.transp_slider.setMaximumSize(new Dimension(250, 20));
    this.transp_slider.setPreferredSize(new Dimension(250, 20));
    final TransparencySliderListener tsl = new TransparencySliderListener();
    this.transp_slider.addChangeListener(tsl);
    this.transp_slider.addMouseListener(tsl);
    for (final KeyListener kl : this.transp_slider.getKeyListeners()) {
        this.transp_slider.removeKeyListener(kl);
    }
    // Tabbed pane on the left
    this.tabs = new JTabbedPane();
    this.tabs.setMinimumSize(new Dimension(250, 300));
    this.tabs.setBackground(Color.white);
    this.tabs.addChangeListener(tabs_listener);
    // Tab 1: Patches
    this.panel_patches = new RollingPanel(this, Patch.class);
    this.addTab("Patches", panel_patches);
    // Tab 2: Profiles
    this.panel_profiles = new RollingPanel(this, Profile.class);
    this.addTab("Profiles", panel_profiles);
    // Tab 3: ZDisplayables
    this.panel_zdispl = new RollingPanel(this, ZDisplayable.class);
    this.addTab("Z space", panel_zdispl);
    // Tab 4: channels
    this.panel_channels = makeTabPanel();
    this.scroll_channels = makeScrollPane(panel_channels);
    this.channels = new Channel[4];
    this.channels[0] = new Channel(this, Channel.MONO);
    this.channels[1] = new Channel(this, Channel.RED);
    this.channels[2] = new Channel(this, Channel.GREEN);
    this.channels[3] = new Channel(this, Channel.BLUE);
    // this.panel_channels.add(this.channels[0]);
    addGBRow(this.panel_channels, this.channels[1], null);
    addGBRow(this.panel_channels, this.channels[2], this.channels[1]);
    addGBRow(this.panel_channels, this.channels[3], this.channels[2]);
    this.addTab("Opacity", scroll_channels);
    // Tab 5: labels
    this.panel_labels = new RollingPanel(this, DLabel.class);
    this.addTab("Labels", panel_labels);
    // Tab 6: layers
    this.panel_layers = makeTabPanel();
    this.scroll_layers = makeScrollPane(panel_layers);
    recreateLayerPanels(layer);
    this.scroll_layers.addMouseWheelListener(canvas);
    this.addTab("Layers", scroll_layers);
    // Tab 7: tool options
    // empty
    this.tool_options = new OptionPanel();
    this.scroll_options = makeScrollPane(this.tool_options);
    this.scroll_options.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    this.addTab("Tool options", this.scroll_options);
    // Tab 8: annotations
    this.annot_editor = new JEditorPane();
    // by default, nothing is selected
    this.annot_editor.setEnabled(false);
    this.annot_editor.setMinimumSize(new Dimension(200, 50));
    this.annot_label = new JLabel("(No selected object)");
    this.annot_panel = makeAnnotationsPanel(this.annot_editor, this.annot_label);
    this.addTab("Annotations", this.annot_panel);
    // Tab 9: filter options
    this.filter_options = createFilterOptionPanel();
    this.scroll_filter_options = makeScrollPane(this.filter_options);
    this.scroll_filter_options.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    this.addTab("Live filter", this.scroll_filter_options);
    this.ht_tabs = new Hashtable<Class<?>, RollingPanel>();
    this.ht_tabs.put(Patch.class, panel_patches);
    this.ht_tabs.put(Profile.class, panel_profiles);
    this.ht_tabs.put(ZDisplayable.class, panel_zdispl);
    this.ht_tabs.put(AreaList.class, panel_zdispl);
    this.ht_tabs.put(Pipe.class, panel_zdispl);
    this.ht_tabs.put(Polyline.class, panel_zdispl);
    this.ht_tabs.put(Treeline.class, panel_zdispl);
    this.ht_tabs.put(AreaTree.class, panel_zdispl);
    this.ht_tabs.put(Connector.class, panel_zdispl);
    this.ht_tabs.put(Ball.class, panel_zdispl);
    this.ht_tabs.put(Dissector.class, panel_zdispl);
    this.ht_tabs.put(DLabel.class, panel_labels);
    this.ht_tabs.put(Stack.class, panel_zdispl);
    // channels not included
    // layers not included
    // tools not included
    // annotations not included
    // Navigator
    this.navigator = new DisplayNavigator(this, layer.getLayerWidth(), layer.getLayerHeight());
    // Layer scroller (to scroll slices)
    int extent = (int) (250.0 / layer.getParent().size());
    if (extent < 10)
        extent = 10;
    this.scroller = new JScrollBar(JScrollBar.HORIZONTAL);
    this.scroller.setModel(new ScrollerModel(layer));
    updateLayerScroller(layer);
    this.scroller.addAdjustmentListener(scroller_listener);
    // LAYOUT
    final GridBagLayout layout = new GridBagLayout();
    final GridBagConstraints c = new GridBagConstraints();
    c.anchor = GridBagConstraints.NORTHWEST;
    c.fill = GridBagConstraints.NONE;
    c.weightx = 0;
    c.weighty = 0;
    c.gridx = 0;
    c.gridy = 0;
    c.ipadx = 0;
    c.ipady = 0;
    c.gridwidth = 1;
    c.gridheight = 1;
    Display.this.all = new JPanel();
    all.setBackground(Color.white);
    all.setLayout(layout);
    c.insets = new Insets(0, 0, 0, 5);
    // 1
    // fixed dimensions
    toolbar_panel = new ToolbarPanel();
    layout.setConstraints(toolbar_panel, c);
    all.add(toolbar_panel);
    // 2
    c.gridy++;
    c.fill = GridBagConstraints.HORIZONTAL;
    layout.setConstraints(transp_slider, c);
    all.add(transp_slider);
    // 3
    c.gridy++;
    c.weighty = 1;
    c.fill = GridBagConstraints.BOTH;
    layout.setConstraints(tabs, c);
    all.add(tabs);
    // 4
    c.gridy++;
    c.weighty = 0;
    c.fill = GridBagConstraints.NONE;
    layout.setConstraints(navigator, c);
    all.add(navigator);
    // 5
    c.gridy++;
    c.fill = GridBagConstraints.HORIZONTAL;
    layout.setConstraints(scroller, c);
    all.add(scroller);
    // Canvas
    this.canvas = new DisplayCanvas(this, (int) Math.ceil(layer.getLayerWidth()), (int) Math.ceil(layer.getLayerHeight()));
    c.insets = new Insets(0, 0, 0, 0);
    c.fill = GridBagConstraints.BOTH;
    c.anchor = GridBagConstraints.NORTHWEST;
    c.gridx = 1;
    c.gridy = 0;
    c.gridheight = GridBagConstraints.REMAINDER;
    c.weightx = 1;
    c.weighty = 1;
    layout.setConstraints(Display.this.canvas, c);
    all.add(canvas);
    // prevent new Displays from screwing up if input is globally disabled
    if (!project.isInputEnabled())
        Display.this.canvas.setReceivesInput(false);
    this.canvas.addComponentListener(canvas_size_listener);
    this.navigator.addMouseWheelListener(canvas);
    this.transp_slider.addKeyListener(canvas);
    // JFrame to show the split pane
    this.frame = ControlWindow.createJFrame(layer.toString());
    this.frame.setBackground(Color.white);
    this.frame.getContentPane().setBackground(Color.white);
    if (IJ.isMacintosh() && IJ.getInstance() != null) {
        // may be needed for Java 1.4 on OS X
        IJ.wait(10);
        this.frame.setMenuBar(ij.Menus.getMenuBar());
    }
    this.frame.addWindowListener(window_listener);
    // this.frame.addComponentListener(display_frame_listener);
    this.frame.getContentPane().add(all);
    if (null != props) {
        // restore canvas
        canvas.setup(mag, srcRect);
        // restore visibility of each channel
        // aka c_alphas_state
        final int cs = ((Integer) props[5]).intValue();
        final int[] sel = new int[4];
        sel[0] = ((cs & 0xff000000) >> 24);
        sel[1] = ((cs & 0xff0000) >> 16);
        sel[2] = ((cs & 0xff00) >> 8);
        sel[3] = (cs & 0xff);
        // restore channel alphas
        Display.this.c_alphas = ((Integer) props[4]).intValue();
        channels[0].setAlpha((float) ((c_alphas & 0xff000000) >> 24) / 255.0f, 0 != sel[0]);
        channels[1].setAlpha((float) ((c_alphas & 0xff0000) >> 16) / 255.0f, 0 != sel[1]);
        channels[2].setAlpha((float) ((c_alphas & 0xff00) >> 8) / 255.0f, 0 != sel[2]);
        channels[3].setAlpha((float) (c_alphas & 0xff) / 255.0f, 0 != sel[3]);
        // restore visibility in the working c_alphas
        Display.this.c_alphas = ((0 != sel[0] ? (int) (255 * channels[0].getAlpha()) : 0) << 24) + ((0 != sel[1] ? (int) (255 * channels[1].getAlpha()) : 0) << 16) + ((0 != sel[2] ? (int) (255 * channels[2].getAlpha()) : 0) << 8) + (0 != sel[3] ? (int) (255 * channels[3].getAlpha()) : 0);
    }
    if (null != active && null != layer) {
        final Rectangle r = active.getBoundingBox();
        r.x -= r.width / 2;
        r.y -= r.height / 2;
        r.width += r.width;
        r.height += r.height;
        if (r.x < 0)
            r.x = 0;
        if (r.y < 0)
            r.y = 0;
        if (r.width > layer.getLayerWidth())
            r.width = (int) layer.getLayerWidth();
        if (r.height > layer.getLayerHeight())
            r.height = (int) layer.getLayerHeight();
        final double magn = layer.getLayerWidth() / (double) r.width;
        canvas.setup(magn, r);
    }
    // add keyListener to the whole frame
    this.tabs.addKeyListener(canvas);
    this.frame.addKeyListener(canvas);
    // create a drag and drop listener
    dnd = new DNDInsertImage(Display.this);
    Utils.invokeLater(new Runnable() {

        @Override
        public void run() {
            Display.this.frame.pack();
            ij.gui.GUI.center(Display.this.frame);
            Display.this.frame.setVisible(true);
            // doesn't get it through events
            ProjectToolbar.setProjectToolbar();
            final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
            if (null != props) {
                // fix positioning outside the screen (dual to single monitor)
                if (p.x >= 0 && p.x < screen.width - 50 && p.y >= 0 && p.y <= screen.height - 50)
                    Display.this.frame.setLocation(p);
                else
                    frame.setLocation(0, 0);
            }
            // fix excessive size
            final Rectangle box = Display.this.frame.getBounds();
            int x = box.x;
            int y = box.y;
            int width = box.width;
            int height = box.height;
            if (box.width > screen.width) {
                x = 0;
                width = screen.width;
            }
            if (box.height > screen.height) {
                y = 0;
                height = screen.height;
            }
            if (x != box.x || y != box.y) {
                // added insets for bad window managers
                Display.this.frame.setLocation(x, y + (0 == y ? 30 : 0));
            }
            if (width != box.width || height != box.height) {
                // added insets for bad window managers
                Display.this.frame.setSize(new Dimension(width - 10, height - 30));
            }
            if (null == props) {
                // try to optimize canvas dimensions and magn
                double magn = layer.getLayerHeight() / screen.height;
                if (magn > 1.0)
                    magn = 1.0;
                long size = 0;
                // limit magnification if appropriate
                for (final Displayable pa : layer.getDisplayables(Patch.class)) {
                    final Rectangle ba = pa.getBoundingBox();
                    size += (long) (ba.width * ba.height);
                }
                if (// 10 Mb
                size > 10000000)
                    // 10 Mb
                    canvas.setInitialMagnification(0.25);
                else {
                    Display.this.frame.setSize(new Dimension((int) (screen.width * 0.66), (int) (screen.height * 0.66)));
                }
            }
            updateTab(panel_patches);
            // re-layout:
            tabs.validate();
            // Set the calibration of the FakeImagePlus to that of the LayerSet
            ((FakeImagePlus) canvas.getFakeImagePlus()).setCalibrationSuper(layer.getParent().getCalibrationCopy());
            updateFrameTitle(layer);
            // Set the FakeImagePlus as the current image
            setTempCurrentImage();
            // start a repainting thread
            if (null != props) {
                // repaint() is unreliable
                canvas.repaint(true);
            }
            ControlWindow.setLookAndFeel();
        }
    });
}
Also used : JPanel(javax.swing.JPanel) GridBagConstraints(java.awt.GridBagConstraints) Insets(java.awt.Insets) GridBagLayout(java.awt.GridBagLayout) JTabbedPane(javax.swing.JTabbedPane) DNDInsertImage(ini.trakem2.utils.DNDInsertImage) Rectangle(java.awt.Rectangle) JScrollBar(javax.swing.JScrollBar) JSlider(javax.swing.JSlider) OptionPanel(ini.trakem2.utils.OptionPanel) JLabel(javax.swing.JLabel) Point(java.awt.Point) Dimension(java.awt.Dimension) Point(java.awt.Point) JEditorPane(javax.swing.JEditorPane) KeyListener(java.awt.event.KeyListener)

Aggregations

Layer (ini.trakem2.display.Layer)61 ArrayList (java.util.ArrayList)52 Patch (ini.trakem2.display.Patch)43 Rectangle (java.awt.Rectangle)34 HashSet (java.util.HashSet)27 ImagePlus (ij.ImagePlus)26 Displayable (ini.trakem2.display.Displayable)25 AffineTransform (java.awt.geom.AffineTransform)23 GenericDialog (ij.gui.GenericDialog)22 Worker (ini.trakem2.utils.Worker)22 Point (mpicbg.models.Point)22 HashMap (java.util.HashMap)21 Point (java.awt.Point)19 Area (java.awt.geom.Area)19 NoninvertibleTransformException (java.awt.geom.NoninvertibleTransformException)18 File (java.io.File)16 Future (java.util.concurrent.Future)16 LayerSet (ini.trakem2.display.LayerSet)15 ExecutorService (java.util.concurrent.ExecutorService)14 Collection (java.util.Collection)13