Search in sources :

Example 26 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class Display method getPopupMenu.

/**
 * Return a context-sensitive popup menu.
 */
protected JPopupMenu getPopupMenu() {
    // get the job canceling dialog
    if (!canvas.isInputEnabled()) {
        return project.getLoader().getJobsPopup(this);
    }
    // create new
    this.popup = new JPopupMenu();
    JMenuItem item = null;
    JMenu menu = null;
    if (mode instanceof InspectPatchTrianglesMode) {
        item = new JMenuItem("Exit inspection");
        item.addActionListener(this);
        popup.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true));
        return popup;
    } else if (canvas.isTransforming()) {
        item = new JMenuItem("Apply transform");
        item.addActionListener(this);
        popup.add(item);
        // dummy, for I don't add a MenuKeyListener, but "works" through the normal key listener. It's here to provide a visual cue
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true));
        item = new JMenuItem("Apply transform propagating to last layer");
        item.addActionListener(this);
        popup.add(item);
        if (layer.getParent().indexOf(layer) == layer.getParent().size() - 1)
            item.setEnabled(false);
        if (!(getMode().getClass() == AffineTransformMode.class || getMode().getClass() == NonLinearTransformMode.class))
            item.setEnabled(false);
        item = new JMenuItem("Apply transform propagating to first layer");
        item.addActionListener(this);
        popup.add(item);
        if (0 == layer.getParent().indexOf(layer))
            item.setEnabled(false);
        if (!(getMode().getClass() == AffineTransformMode.class || getMode().getClass() == NonLinearTransformMode.class))
            item.setEnabled(false);
        item = new JMenuItem("Cancel transform");
        item.addActionListener(this);
        popup.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true));
        item = new JMenuItem("Specify transform...");
        item.addActionListener(this);
        popup.add(item);
        if (getMode().getClass() != AffineTransformMode.class)
            item.setEnabled(false);
        if (getMode().getClass() == ManualAlignMode.class) {
            final JMenuItem lexport = new JMenuItem("Export landmarks");
            popup.add(lexport);
            final JMenuItem limport = new JMenuItem("Import landmarks");
            popup.add(limport);
            final ActionListener a = new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent ae) {
                    final ManualAlignMode mam = (ManualAlignMode) getMode();
                    final Object source = ae.getSource();
                    if (lexport == source) {
                        mam.exportLandmarks();
                    } else if (limport == source) {
                        mam.importLandmarks();
                    }
                }
            };
            lexport.addActionListener(a);
            limport.addActionListener(a);
        }
        return popup;
    }
    final Class<?> aclass = null == active ? null : active.getClass();
    if (null != active) {
        if (Profile.class == aclass) {
            item = new JMenuItem("Duplicate, link and send to next layer");
            item.addActionListener(this);
            popup.add(item);
            Layer nl = layer.getParent().next(layer);
            if (nl == layer)
                item.setEnabled(false);
            item = new JMenuItem("Duplicate, link and send to previous layer");
            item.addActionListener(this);
            popup.add(item);
            nl = layer.getParent().previous(layer);
            if (nl == layer)
                item.setEnabled(false);
            menu = new JMenu("Duplicate, link and send to");
            int i = 1;
            for (final Layer la : layer.getParent().getLayers()) {
                // TODO should label which layers contain Profile instances linked to the one being duplicated
                item = new JMenuItem(i + ": z = " + la.getZ());
                // TODO should label which layers contain Profile instances linked to the one being duplicated
                item.addActionListener(this);
                // TODO should label which layers contain Profile instances linked to the one being duplicated
                menu.add(item);
                if (la == this.layer)
                    item.setEnabled(false);
                i++;
            }
            popup.add(menu);
            item = new JMenuItem("Duplicate, link and send to...");
            item.addActionListener(this);
            popup.add(item);
            popup.addSeparator();
            item = new JMenuItem("Unlink from images");
            item.addActionListener(this);
            popup.add(item);
            // isLinked() checks if it's linked to a Patch in its own layer
            if (!active.isLinked())
                item.setEnabled(false);
            item = new JMenuItem("Show in 3D");
            item.addActionListener(this);
            popup.add(item);
            popup.addSeparator();
        } else if (Patch.class == aclass) {
            final JMenu m = new JMenu("Patch");
            item = new JMenuItem("Fill ROI in alpha mask");
            item.addActionListener(this);
            m.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, 0));
            item.setEnabled(null != getRoi());
            item = new JMenuItem("Fill inverse ROI in alpha mask");
            item.addActionListener(this);
            m.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.SHIFT_MASK));
            item.setEnabled(null != getRoi());
            item = new JMenuItem("Remove alpha mask");
            item.addActionListener(this);
            m.add(item);
            if (!((Patch) active).hasAlphaMask())
                item.setEnabled(false);
            item = new JMenuItem("Unlink from images");
            item.addActionListener(this);
            m.add(item);
            if (!active.isLinked(Patch.class))
                item.setEnabled(false);
            if (((Patch) active).isStack()) {
                item = new JMenuItem("Unlink slices");
                item.addActionListener(this);
                m.add(item);
            }
            final int n_sel_patches = selection.getSelected(Patch.class).size();
            item = new JMenuItem("Snap");
            item.addActionListener(this);
            m.add(item);
            item.setEnabled(1 == n_sel_patches);
            item = new JMenuItem("Montage");
            item.addActionListener(this);
            m.add(item);
            item.setEnabled(n_sel_patches > 1);
            item = new JMenuItem("Lens correction");
            item.addActionListener(this);
            m.add(item);
            item.setEnabled(n_sel_patches > 1);
            item = new JMenuItem("Blend");
            item.addActionListener(this);
            m.add(item);
            item.setEnabled(n_sel_patches > 1);
            item = new JMenuItem("Open image");
            item.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent e) {
                    for (final Patch p : selection.get(Patch.class)) {
                        p.getImagePlus().show();
                    }
                }
            });
            m.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.SHIFT_MASK, true));
            item = new JMenuItem("Open original image");
            item.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent e) {
                    for (final Patch p : selection.get(Patch.class)) {
                        p.getProject().getLoader().releaseToFit(p.getOWidth(), p.getOHeight(), p.getType(), 5);
                        p.getProject().getLoader().openImagePlus(p.getImageFilePath()).show();
                    }
                }
            });
            item = new JMenuItem("View volume");
            item.addActionListener(this);
            m.add(item);
            final HashSet<Displayable> hs = active.getLinked(Patch.class);
            if (null == hs || 0 == hs.size())
                item.setEnabled(false);
            item = new JMenuItem("View orthoslices");
            item.addActionListener(this);
            m.add(item);
            // if no Patch instances among the directly linked, then it's not a stack
            if (null == hs || 0 == hs.size())
                item.setEnabled(false);
            popup.add(m);
            popup.addSeparator();
        } else {
            item = new JMenuItem("Unlink");
            item.addActionListener(this);
            popup.add(item);
            item = new JMenuItem("Show in 3D");
            item.addActionListener(this);
            popup.add(item);
            popup.addSeparator();
        }
        if (AreaList.class == aclass) {
            final ArrayList<?> al = selection.getSelected();
            int n = 0;
            for (final Iterator<?> it = al.iterator(); it.hasNext(); ) {
                if (it.next().getClass() == AreaList.class)
                    n++;
            }
            item = new JMenuItem("Merge");
            item.addActionListener(this);
            popup.add(item);
            if (n < 2)
                item.setEnabled(false);
            item = new JMenuItem("Split");
            item.addActionListener(this);
            popup.add(item);
            if (n < 1)
                item.setEnabled(false);
            addAreaListAreasMenu(popup, active);
            popup.addSeparator();
        } else if (Pipe.class == aclass) {
            item = new JMenuItem("Reverse point order");
            item.addActionListener(this);
            popup.add(item);
            popup.addSeparator();
        } else if (Treeline.class == aclass || AreaTree.class == aclass) {
            if (AreaTree.class == aclass)
                addAreaTreeAreasMenu(popup, (AreaTree) active);
            item = new JMenuItem("Reroot");
            item.addActionListener(this);
            popup.add(item);
            item = new JMenuItem("Part subtree");
            item.addActionListener(this);
            popup.add(item);
            item = new JMenuItem("Join");
            item.addActionListener(this);
            popup.add(item);
            item = new JMenuItem("Show tabular view");
            item.addActionListener(this);
            popup.add(item);
            final Collection<Tree> trees = selection.get(Tree.class);
            // 
            final JMenu nodeMenu = new JMenu("Nodes");
            item = new JMenuItem("Mark");
            item.addActionListener(this);
            nodeMenu.add(item);
            item = new JMenuItem("Clear marks (selected Trees)");
            item.addActionListener(this);
            nodeMenu.add(item);
            final JMenuItem nodeColor = new JMenuItem("Color...");
            nodeMenu.add(nodeColor);
            nodeColor.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.SHIFT_MASK, true));
            final JMenuItem nodePairColor = new JMenuItem("Color path between two nodes tagged as...");
            nodeMenu.add(nodePairColor);
            final JMenuItem nodeRadius = active instanceof Treeline ? new JMenuItem("Radius...") : null;
            if (null != nodeRadius) {
                nodeMenu.add(nodeRadius);
                nodeRadius.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0, true));
            }
            final JMenuItem removeAllTags = new JMenuItem("Drop all tags (selected trees)");
            nodeMenu.add(removeAllTags);
            final JMenuItem removeTag = new JMenuItem("Drop all occurrences of tag...");
            nodeMenu.add(removeTag);
            final JMenuItem colorizeByNodeCentrality = new JMenuItem("Colorize by node betweenness centrality");
            nodeMenu.add(colorizeByNodeCentrality);
            final JMenuItem colorizeByBranchCentrality = new JMenuItem("Colorize by branch betweenness centrality");
            nodeMenu.add(colorizeByBranchCentrality);
            popup.add(nodeMenu);
            final ActionListener ln = new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent ae) {
                    if (null == active) {
                        Utils.showMessage("No tree selected!");
                        return;
                    }
                    if (!(active instanceof Tree)) {
                        Utils.showMessage("The selected object is not a Tree!");
                        return;
                    }
                    final Tree tree = (Tree) active;
                    final Object src = ae.getSource();
                    // 
                    if (src == nodeColor) {
                        final Node nd = tree.getLastVisited();
                        if (null == nd) {
                            Utils.showMessage("Select a node first by clicking on it\nor moving the mouse over it and pushing 'g'.");
                            return;
                        }
                        // sets an undo step
                        tree.adjustNodeColors(nd);
                    } else if (src == nodePairColor) {
                        final TreeMap<String, Tag> sm = getTags(tree);
                        if (null == sm)
                            return;
                        if (1 == sm.size()) {
                            Utils.showMessage("Need at least two different tags in the tree!");
                            return;
                        }
                        final Color color = tree.getColor();
                        final GenericDialog gd = new GenericDialog("Node colors");
                        gd.addSlider("Red: ", 0, 255, color.getRed());
                        gd.addSlider("Green: ", 0, 255, color.getGreen());
                        gd.addSlider("Blue: ", 0, 255, color.getBlue());
                        final String[] stags = asStrings(sm);
                        sm.keySet().toArray(stags);
                        gd.addChoice("Upstream tag:", stags, stags[0]);
                        gd.addChoice("Downstream tag:", stags, stags[1]);
                        gd.showDialog();
                        if (gd.wasCanceled())
                            return;
                        final Color newColor = new Color((int) gd.getNextNumber(), (int) gd.getNextNumber(), (int) gd.getNextNumber());
                        final Tag upstreamTag = sm.get(gd.getNextChoice());
                        final Tag downstreamTag = sm.get(gd.getNextChoice());
                        final List<Tree<?>.NodePath> pairs = tree.findTaggedPairs(upstreamTag, downstreamTag);
                        if (null == pairs || pairs.isEmpty()) {
                            Utils.showMessage("No pairs found for '" + upstreamTag + "' and '" + downstreamTag + "'");
                            return;
                        }
                        getLayerSet().addDataEditStep(tree);
                        for (final Tree<?>.NodePath pair : pairs) {
                            for (final Node<?> nd : pair.path) {
                                nd.setColor(newColor);
                            }
                        }
                        getLayerSet().addDataEditStep(tree);
                        Display.repaint();
                    } else if (src == nodeRadius) {
                        if (!(tree instanceof Treeline))
                            return;
                        final Node nd = tree.getLastVisited();
                        if (null == nd) {
                            Utils.showMessage("Select a node first by clicking on it\nor moving the mouse over it and pushing 'g'.");
                            return;
                        }
                        // sets an undo step
                        ((Treeline) tree).askAdjustRadius(nd);
                    } else if (src == removeAllTags) {
                        if (!Utils.check("Really remove all tags from all selected trees?"))
                            return;
                        final List<Tree> sel = selection.get(Tree.class);
                        getLayerSet().addDataEditStep(new HashSet<Displayable>(sel));
                        try {
                            for (final Tree t : sel) {
                                t.dropAllTags();
                            }
                            // current state
                            getLayerSet().addDataEditStep(new HashSet<Displayable>(sel));
                        } catch (final Exception e) {
                            getLayerSet().undoOneStep();
                            IJError.print(e);
                        }
                        Display.repaint();
                    } else if (src == removeTag) {
                        final TreeMap<String, Tag> tags = getTags(tree);
                        final String[] ts = asStrings(tags);
                        final GenericDialog gd = new GenericDialog("Remove tags");
                        gd.addChoice("Tag:", ts, ts[0]);
                        final String[] c = new String[] { "Active tree", "All selected trees and connectors", "All trees and connectors" };
                        gd.addChoice("From: ", c, c[0]);
                        gd.showDialog();
                        if (gd.wasCanceled())
                            return;
                        final HashSet<Displayable> ds = new HashSet<Displayable>();
                        final Tag tag = tags.get(gd.getNextChoice());
                        switch(gd.getNextChoiceIndex()) {
                            case 0:
                                ds.add(tree);
                                break;
                            case 1:
                                ds.addAll(selection.get(Tree.class));
                            case 2:
                                ds.addAll(getLayerSet().getZDisplayables(Tree.class, true));
                        }
                        getLayerSet().addDataEditStep(ds);
                        try {
                            for (final Displayable d : ds) {
                                final Tree t = (Tree) d;
                                t.removeTag(tag);
                            }
                            getLayerSet().addDataEditStep(ds);
                        } catch (final Exception e) {
                            getLayerSet().undoOneStep();
                            IJError.print(e);
                        }
                        Display.repaint();
                    } else if (src == colorizeByNodeCentrality) {
                        final List<Tree> ts = selection.get(Tree.class);
                        final HashSet<Tree> ds = new HashSet<Tree>(ts);
                        getLayerSet().addDataEditStep(ds);
                        try {
                            for (final Tree t : ts) {
                                t.colorizeByNodeBetweennessCentrality();
                            }
                            getLayerSet().addDataEditStep(ds);
                            Display.repaint();
                        } catch (final Exception e) {
                            getLayerSet().undoOneStep();
                            IJError.print(e);
                        }
                    } else if (src == colorizeByBranchCentrality) {
                        final List<Tree> ts = selection.get(Tree.class);
                        final HashSet<Tree> ds = new HashSet<Tree>(ts);
                        getLayerSet().addDataEditStep(ds);
                        try {
                            for (final Tree t : ts) {
                                t.colorizeByBranchBetweennessCentrality(2);
                            }
                            getLayerSet().addDataEditStep(ds);
                            Display.repaint();
                        } catch (final Exception e) {
                            getLayerSet().undoOneStep();
                            IJError.print(e);
                        }
                    }
                }
            };
            for (final JMenuItem a : new JMenuItem[] { nodeColor, nodePairColor, nodeRadius, removeAllTags, removeTag, colorizeByNodeCentrality, colorizeByBranchCentrality }) {
                if (null == a)
                    continue;
                a.addActionListener(ln);
            }
            // 
            final JMenu review = new JMenu("Review");
            final JMenuItem tgenerate = new JMenuItem("Generate review stacks (selected Trees)");
            review.add(tgenerate);
            tgenerate.setEnabled(trees.size() > 0);
            final JMenuItem tslab = new JMenuItem("Generate review stack for current slab");
            review.add(tslab);
            final JMenuItem tsubtree = new JMenuItem("Generate review stacks for subtree");
            review.add(tsubtree);
            final JMenuItem tremove = new JMenuItem("Remove reviews (selected Trees)");
            review.add(tremove);
            tremove.setEnabled(trees.size() > 0);
            final JMenuItem tconnectors = new JMenuItem("View table of outgoing/incoming connectors");
            review.add(tconnectors);
            final ActionListener l = new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent ae) {
                    if (!Utils.check("Really " + ae.getActionCommand())) {
                        return;
                    }
                    dispatcher.exec(new Runnable() {

                        @Override
                        public void run() {
                            int count = 0;
                            for (final Tree<?> t : trees) {
                                Utils.log("Processing " + (++count) + "/" + trees.size());
                                Bureaucrat bu = null;
                                if (ae.getSource() == tgenerate)
                                    bu = t.generateAllReviewStacks();
                                else if (ae.getSource() == tremove)
                                    bu = t.removeReviews();
                                else if (ae.getSource() == tslab) {
                                    final Point po = canvas.consumeLastPopupPoint();
                                    Utils.log2(po, layer, 1.0);
                                    bu = t.generateReviewStackForSlab(po.x, po.y, Display.this.layer, 1.0);
                                } else if (ae.getSource() == tsubtree) {
                                    final Point po = canvas.consumeLastPopupPoint();
                                    bu = t.generateSubtreeReviewStacks(po.x, po.y, Display.this.layer, 1.0);
                                }
                                if (null != bu)
                                    try {
                                        bu.getWorker().join();
                                    } catch (final InterruptedException ie) {
                                        return;
                                    }
                            }
                        }
                    });
                }
            };
            for (final JMenuItem c : new JMenuItem[] { tgenerate, tslab, tsubtree, tremove }) c.addActionListener(l);
            tconnectors.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent ae) {
                    for (final Tree<?> t : trees) TreeConnectorsView.create(t);
                }
            });
            popup.add(review);
            final JMenu go = new JMenu("Go");
            item = new JMenuItem("Previous branch node or start");
            item.addActionListener(this);
            go.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, 0, true));
            item = new JMenuItem("Next branch node or end");
            item.addActionListener(this);
            go.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0, true));
            item = new JMenuItem("Root");
            item.addActionListener(this);
            go.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0, true));
            go.addSeparator();
            item = new JMenuItem("Last added node");
            item.addActionListener(this);
            go.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, 0, true));
            item = new JMenuItem("Last edited node");
            item.addActionListener(this);
            go.add(item);
            item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, true));
            popup.add(go);
            final JMenu tmeasure = new JMenu("Measure");
            final JMenuItem dist_to_root = new JMenuItem("Distance from this node to root");
            tmeasure.add(dist_to_root);
            final JMenuItem dist_to_tag = new JMenuItem("Distance from this node to all nodes tagged as...");
            tmeasure.add(dist_to_tag);
            final JMenuItem dist_to_mark = new JMenuItem("Distance from this node to the marked node");
            tmeasure.add(dist_to_mark);
            final JMenuItem dist_pairs = new JMenuItem("Shortest distances between all pairs of nodes tagged as...");
            tmeasure.add(dist_pairs);
            final ActionListener tma = getTreePathMeasureListener((Tree<?>) active);
            for (final JMenuItem mi : new JMenuItem[] { dist_to_root, dist_to_tag, dist_to_mark, dist_pairs }) {
                mi.addActionListener(tma);
            }
            popup.add(tmeasure);
            final String[] name = new String[] { AreaTree.class.getSimpleName(), Treeline.class.getSimpleName() };
            if (Treeline.class == aclass) {
                final String a = name[0];
                name[0] = name[1];
                name[1] = a;
            }
            item = new JMenuItem("Duplicate " + name[0] + " as " + name[1]);
            item.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent e) {
                    Bureaucrat.createAndStart(new Worker.Task("Converting") {

                        @Override
                        public void exec() {
                            try {
                                getLayerSet().addChangeTreesStep();
                                final Map<Tree<?>, Tree<?>> m = Tree.duplicateAs(selection.getSelected(), (Class<Tree<?>>) (Treeline.class == aclass ? AreaTree.class : Treeline.class));
                                if (m.isEmpty()) {
                                    getLayerSet().removeLastUndoStep();
                                } else {
                                    getLayerSet().addChangeTreesStep();
                                }
                            } catch (final Exception e) {
                                IJError.print(e);
                            }
                        }
                    }, getProject());
                }
            });
            popup.add(item);
            popup.addSeparator();
        } else if (Connector.class == aclass) {
            item = new JMenuItem("Merge");
            item.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(final ActionEvent ae) {
                    if (null == getActive() || getActive().getClass() != Connector.class) {
                        Utils.log("Active object must be a Connector!");
                        return;
                    }
                    final List<Connector> col = selection.get(Connector.class);
                    if (col.size() < 2) {
                        Utils.log("Select more than one Connector!");
                        return;
                    }
                    if (col.get(0) != getActive()) {
                        if (col.remove(getActive())) {
                            col.add(0, (Connector) getActive());
                        } else {
                            Utils.log("ERROR: cannot find active object in selection list!");
                            return;
                        }
                    }
                    Bureaucrat.createAndStart(new Worker.Task("Merging connectors") {

                        @Override
                        public void exec() {
                            getLayerSet().addChangeTreesStep();
                            Connector base = null;
                            try {
                                base = Connector.merge(col);
                            } catch (final Exception e) {
                                IJError.print(e);
                            }
                            if (null == base) {
                                Utils.log("ERROR: could not merge connectors!");
                                getLayerSet().undoOneStep();
                            } else {
                                getLayerSet().addChangeTreesStep();
                            }
                            Display.repaint();
                        }
                    }, getProject());
                }
            });
            popup.add(item);
            item.setEnabled(selection.getSelected(Connector.class).size() > 1);
            popup.addSeparator();
        }
        item = new JMenuItem("Duplicate");
        item.addActionListener(this);
        popup.add(item);
        item = new JMenuItem("Color...");
        item.addActionListener(this);
        popup.add(item);
        if (active instanceof LayerSet)
            item.setEnabled(false);
        if (active.isLocked()) {
            item = new JMenuItem("Unlock");
            item.addActionListener(this);
            popup.add(item);
        } else {
            item = new JMenuItem("Lock");
            item.addActionListener(this);
            popup.add(item);
        }
        menu = new JMenu("Move");
        popup.addSeparator();
        final LayerSet ls = layer.getParent();
        item = new JMenuItem("Move to top");
        item.addActionListener(this);
        menu.add(item);
        // this is just to draw the key name by the menu; it does not incur on any event being generated (that I know if), and certainly not any event being listened to by TrakEM2.
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0, true));
        if (ls.isTop(active))
            item.setEnabled(false);
        item = new JMenuItem("Move up");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0, true));
        if (ls.isTop(active))
            item.setEnabled(false);
        item = new JMenuItem("Move down");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0, true));
        if (ls.isBottom(active))
            item.setEnabled(false);
        item = new JMenuItem("Move to bottom");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0, true));
        if (ls.isBottom(active))
            item.setEnabled(false);
        popup.add(menu);
        popup.addSeparator();
        item = new JMenuItem("Delete...");
        item.addActionListener(this);
        popup.add(item);
        try {
            if (Patch.class == aclass) {
                if (!active.isOnlyLinkedTo(Patch.class)) {
                    item.setEnabled(false);
                }
            }
        } catch (final Exception e) {
            IJError.print(e);
            item.setEnabled(false);
        }
        if (Patch.class == aclass) {
            item = new JMenuItem("Revert");
            item.addActionListener(this);
            popup.add(item);
            if (null == ((Patch) active).getOriginalPath())
                item.setEnabled(false);
            popup.addSeparator();
        }
        item = new JMenuItem("Properties...");
        item.addActionListener(this);
        popup.add(item);
        item = new JMenuItem("Show centered");
        item.addActionListener(this);
        popup.add(item);
        popup.addSeparator();
        if (!(active instanceof ZDisplayable)) {
            final int i_layer = layer.getParent().indexOf(layer);
            final int n_layers = layer.getParent().size();
            item = new JMenuItem("Send to previous layer");
            item.addActionListener(this);
            popup.add(item);
            if (1 == n_layers || 0 == i_layer || active.isLinked())
                item.setEnabled(false);
            else // check if the active is a profile and contains a link to another profile in the layer it is going to be sent to, or it is linked
            if (active instanceof Profile && !active.canSendTo(layer.getParent().previous(layer)))
                item.setEnabled(false);
            item = new JMenuItem("Send to next layer");
            item.addActionListener(this);
            popup.add(item);
            if (1 == n_layers || n_layers - 1 == i_layer || active.isLinked())
                item.setEnabled(false);
            else if (active instanceof Profile && !active.canSendTo(layer.getParent().next(layer)))
                item.setEnabled(false);
            menu = new JMenu("Send linked group to...");
            if (active.hasLinkedGroupWithinLayer(this.layer)) {
                int i = 1;
                for (final Layer la : ls.getLayers()) {
                    String layer_title = i + ": " + la.getTitle();
                    if (-1 == layer_title.indexOf(' '))
                        layer_title += " ";
                    item = new JMenuItem(layer_title);
                    item.addActionListener(this);
                    menu.add(item);
                    if (la == this.layer)
                        item.setEnabled(false);
                    i++;
                }
                popup.add(menu);
            } else {
                menu.setEnabled(false);
            // Utils.log("Active's linked group not within layer.");
            }
            popup.add(menu);
            popup.addSeparator();
        }
    }
    item = new JMenuItem("Undo");
    item.addActionListener(this);
    popup.add(item);
    if (!layer.getParent().canUndo() || canvas.isTransforming())
        item.setEnabled(false);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Utils.getControlModifier(), true));
    item = new JMenuItem("Redo");
    item.addActionListener(this);
    popup.add(item);
    if (!layer.getParent().canRedo() || canvas.isTransforming())
        item.setEnabled(false);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.SHIFT_MASK | Utils.getControlModifier(), true));
    popup.addSeparator();
    try {
        menu = new JMenu("Hide/Unhide");
        item = new JMenuItem("Hide deselected");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, Event.SHIFT_MASK, true));
        boolean none = 0 == selection.getNSelected();
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Hide deselected except images");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, Event.SHIFT_MASK | Event.ALT_MASK, true));
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Hide selected");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, 0, true));
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().containsDisplayable(DLabel.class);
        item = new JMenuItem("Hide all labels");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all labels");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(AreaList.class);
        item = new JMenuItem("Hide all arealists");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all arealists");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.contains(Profile.class);
        item = new JMenuItem("Hide all profiles");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all profiles");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(Pipe.class);
        item = new JMenuItem("Hide all pipes");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all pipes");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(Polyline.class);
        item = new JMenuItem("Hide all polylines");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all polylines");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(Treeline.class);
        item = new JMenuItem("Hide all treelines");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all treelines");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(AreaTree.class);
        item = new JMenuItem("Hide all areatrees");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all areatrees");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(Ball.class);
        item = new JMenuItem("Hide all balls");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all balls");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().contains(Connector.class);
        item = new JMenuItem("Hide all connectors");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all connectors");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        none = !layer.getParent().containsDisplayable(Patch.class);
        item = new JMenuItem("Hide all images");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Unhide all images");
        item.addActionListener(this);
        menu.add(item);
        if (none)
            item.setEnabled(false);
        item = new JMenuItem("Hide all but images");
        item.addActionListener(this);
        menu.add(item);
        item = new JMenuItem("Unhide all");
        item.addActionListener(this);
        menu.add(item);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, Event.ALT_MASK, true));
        popup.add(menu);
    } catch (final Exception e) {
        IJError.print(e);
    }
    // plugins, if any
    Utils.addPlugIns(popup, "Display", project, new Callable<Displayable>() {

        @Override
        public Displayable call() {
            return Display.this.getActive();
        }
    });
    final JMenu align_menu = new JMenu("Align");
    item = new JMenuItem("Align stack slices");
    item.addActionListener(this);
    align_menu.add(item);
    if (selection.isEmpty() || !(getActive().getClass() == Patch.class && ((Patch) getActive()).isStack()))
        item.setEnabled(false);
    item = new JMenuItem("Align layers");
    item.addActionListener(this);
    align_menu.add(item);
    if (1 == layer.getParent().size())
        item.setEnabled(false);
    item = new JMenuItem("Align layers manually with landmarks");
    item.addActionListener(this);
    align_menu.add(item);
    if (1 == layer.getParent().size())
        item.setEnabled(false);
    item = new JMenuItem("Align multi-layer mosaic");
    item.addActionListener(this);
    align_menu.add(item);
    if (1 == layer.getParent().size())
        item.setEnabled(false);
    item = new JMenuItem("Montage all images in this layer");
    item.addActionListener(this);
    align_menu.add(item);
    if (layer.getDisplayables(Patch.class).size() < 2)
        item.setEnabled(false);
    item = new JMenuItem("Montage selected images");
    item.addActionListener(this);
    align_menu.add(item);
    if (selection.getSelected(Patch.class).size() < 2)
        item.setEnabled(false);
    item = new JMenuItem("Montage multiple layers");
    item.addActionListener(this);
    align_menu.add(item);
    popup.add(align_menu);
    final JMenuItem st = new JMenu("Transform");
    final StartTransformMenuListener tml = new StartTransformMenuListener();
    item = new JMenuItem("Transform (affine)");
    item.addActionListener(tml);
    st.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0, true));
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Transform (non-linear)");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, Event.SHIFT_MASK, true));
    item = new JMenuItem("Cancel transform");
    st.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true));
    // just added as a self-documenting cue; no listener
    item.setEnabled(false);
    item = new JMenuItem("Remove rotation, scaling and shear (selected images)");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Remove rotation, scaling and shear layer-wise");
    item.addActionListener(tml);
    st.add(item);
    item = new JMenuItem("Remove coordinate transforms (selected images)");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Remove coordinate transforms layer-wise");
    item.addActionListener(tml);
    st.add(item);
    item = new JMenuItem("Adjust mesh resolution (selected images)");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Adjust mesh resolution layer-wise");
    item.addActionListener(tml);
    st.add(item);
    item = new JMenuItem("Set coordinate transform of selected image to other selected images");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Set coordinate transform of selected image layer-wise");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Set affine transform of selected image to other selected images");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    item = new JMenuItem("Set affine transform of selected image layer-wise");
    item.addActionListener(tml);
    st.add(item);
    if (null == active)
        item.setEnabled(false);
    popup.add(st);
    final JMenu link_menu = new JMenu("Link");
    item = new JMenuItem("Link images...");
    item.addActionListener(this);
    link_menu.add(item);
    item = new JMenuItem("Unlink all selected images");
    item.addActionListener(this);
    link_menu.add(item);
    item.setEnabled(selection.getSelected(Patch.class).size() > 0);
    item = new JMenuItem("Unlink all");
    item.addActionListener(this);
    link_menu.add(item);
    popup.add(link_menu);
    final JMenu adjust_menu = new JMenu("Adjust images");
    item = new JMenuItem("Enhance contrast layer-wise...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item = new JMenuItem("Enhance contrast (selected images)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Adjust image filters (selected images)");
    item.addActionListener(this);
    adjust_menu.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Set Min and Max layer-wise...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item = new JMenuItem("Set Min and Max (selected images)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Adjust min and max (selected images)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_J, 0));
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Mask image borders (layer-wise)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item = new JMenuItem("Mask image borders (selected images)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Remove alpha masks (layer-wise)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item = new JMenuItem("Remove alpha masks (selected images)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Split images under polyline ROI");
    item.addActionListener(this);
    adjust_menu.add(item);
    final Roi roi = canvas.getFakeImagePlus().getRoi();
    if (null == roi || !(roi.getType() == Roi.POLYLINE || roi.getType() == Roi.FREELINE))
        item.setEnabled(false);
    item = new JMenuItem("Blend (layer-wise)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item = new JMenuItem("Blend (selected images)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Match intensities (layer-wise)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    item = new JMenuItem("Remove intensity maps (layer-wise)...");
    item.addActionListener(this);
    adjust_menu.add(item);
    popup.add(adjust_menu);
    final JMenu script = new JMenu("Script");
    final MenuScriptListener msl = new MenuScriptListener();
    item = new JMenuItem("Set preprocessor script layer-wise...");
    item.addActionListener(msl);
    script.add(item);
    item = new JMenuItem("Set preprocessor script (selected images)...");
    item.addActionListener(msl);
    script.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    item = new JMenuItem("Remove preprocessor script layer-wise...");
    item.addActionListener(msl);
    script.add(item);
    item = new JMenuItem("Remove preprocessor script (selected images)...");
    item.addActionListener(msl);
    script.add(item);
    if (selection.isEmpty())
        item.setEnabled(false);
    popup.add(script);
    menu = new JMenu("Import");
    item = new JMenuItem("Import image");
    item.addActionListener(this);
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK & Event.SHIFT_MASK, true));
    item = new JMenuItem("Import stack...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Import stack with landmarks...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Import grid...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Import sequence as grid...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Import from text file...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Import labels as arealists...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Tags ...");
    item.addActionListener(this);
    menu.add(item);
    popup.add(menu);
    menu = new JMenu("Export");
    final boolean has_arealists = layer.getParent().contains(AreaList.class);
    item = new JMenuItem("Make flat image...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Arealists as labels (tif)");
    item.addActionListener(this);
    menu.add(item);
    item.setEnabled(has_arealists);
    item = new JMenuItem("Arealists as labels (amira)");
    item.addActionListener(this);
    menu.add(item);
    item.setEnabled(has_arealists);
    item = new JMenuItem("Image stack under selected Arealist");
    item.addActionListener(this);
    menu.add(item);
    item.setEnabled(null != active && AreaList.class == active.getClass());
    item = new JMenuItem("Fly through selected Treeline/AreaTree");
    item.addActionListener(this);
    menu.add(item);
    item.setEnabled(null != active && Tree.class.isInstance(active));
    item = new JMenuItem("Tags...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Connectivity graph...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("NeuroML...");
    item.addActionListener(this);
    menu.add(item);
    popup.add(menu);
    menu = new JMenu("Display");
    item = new JMenuItem("Resize canvas/LayerSet...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Autoresize canvas/LayerSet");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Resize canvas/LayerSet to ROI");
    item.addActionListener(this);
    menu.add(item);
    item.setEnabled(null != canvas.getFakeImagePlus().getRoi());
    item = new JMenuItem("Properties ...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Calibration...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Grid overlay...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Adjust snapping parameters...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Adjust fast-marching parameters...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Adjust arealist paint parameters...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Show current 2D position in 3D");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Show layers as orthoslices in 3D");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Inspect image mesh triangles");
    item.addActionListener(this);
    menu.add(item);
    popup.add(menu);
    menu = new JMenu("Project");
    this.project.getLoader().setupMenuItems(menu, this.getProject());
    item = new JMenuItem("Project properties...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Create subproject");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Create sibling project with retiled layers");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Release memory...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Flush image cache");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Regenerate all mipmaps");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Regenerate mipmaps (selected images)");
    item.addActionListener(this);
    menu.add(item);
    menu.addSeparator();
    item = new JMenuItem("Measurement options...");
    item.addActionListener(this);
    menu.add(item);
    popup.add(menu);
    menu = new JMenu("Selection");
    item = new JMenuItem("Select all");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Select all visible");
    item.addActionListener(this);
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, Utils.getControlModifier(), true));
    if (0 == layer.getDisplayableList().size() && 0 == layer.getParent().getDisplayableList().size())
        item.setEnabled(false);
    item = new JMenuItem("Select all that match...");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Select none");
    item.addActionListener(this);
    menu.add(item);
    if (0 == selection.getNSelected())
        item.setEnabled(false);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true));
    final JMenu bytype = new JMenu("Select all by type");
    item = new JMenuItem("AreaList");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("AreaTree");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Ball");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Connector");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Dissector");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Image");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Pipe");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Polyline");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Profile");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Text");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    item = new JMenuItem("Treeline");
    item.addActionListener(bytypelistener);
    bytype.add(item);
    menu.add(bytype);
    item = new JMenuItem("Restore selection");
    item.addActionListener(this);
    menu.add(item);
    item = new JMenuItem("Select under ROI");
    item.addActionListener(this);
    menu.add(item);
    if (canvas.getFakeImagePlus().getRoi() == null)
        item.setEnabled(false);
    final JMenu graph = new JMenu("Graph");
    final GraphMenuListener gl = new GraphMenuListener();
    item = new JMenuItem("Select outgoing Connectors");
    item.addActionListener(gl);
    graph.add(item);
    item = new JMenuItem("Select incoming Connectors");
    item.addActionListener(gl);
    graph.add(item);
    item = new JMenuItem("Select downstream targets");
    item.addActionListener(gl);
    graph.add(item);
    item = new JMenuItem("Select upstream targets");
    item.addActionListener(gl);
    graph.add(item);
    graph.setEnabled(!selection.isEmpty());
    menu.add(graph);
    item = new JMenuItem("Measure");
    item.addActionListener(this);
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0, true));
    item.setEnabled(!selection.isEmpty());
    popup.add(menu);
    menu = new JMenu("Tool");
    item = new JMenuItem("Rectangular ROI");
    item.addActionListener(new SetToolListener(Toolbar.RECTANGLE));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0, true));
    item = new JMenuItem("Polygon ROI");
    item.addActionListener(new SetToolListener(Toolbar.POLYGON));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0, true));
    item = new JMenuItem("Freehand ROI");
    item.addActionListener(new SetToolListener(Toolbar.FREEROI));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0, true));
    item = new JMenuItem("Text");
    item.addActionListener(new SetToolListener(Toolbar.TEXT));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0, true));
    item = new JMenuItem("Magnifier glass");
    item.addActionListener(new SetToolListener(Toolbar.MAGNIFIER));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0, true));
    item = new JMenuItem("Hand");
    item.addActionListener(new SetToolListener(Toolbar.HAND));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0, true));
    item = new JMenuItem("Select");
    item.addActionListener(new SetToolListener(ProjectToolbar.SELECT));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0, true));
    item = new JMenuItem("Pencil");
    item.addActionListener(new SetToolListener(ProjectToolbar.PENCIL));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0, true));
    item = new JMenuItem("Pen");
    item.addActionListener(new SetToolListener(ProjectToolbar.PEN));
    menu.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0, true));
    popup.add(menu);
    item = new JMenuItem("Search...");
    item.addActionListener(this);
    popup.add(item);
    item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Utils.getControlModifier(), true));
    // canvas.add(popup);
    return popup;
}
Also used : AlignTask(mpicbg.trakem2.align.AlignTask) AlignLayersTask(mpicbg.trakem2.align.AlignLayersTask) DistortionCorrectionTask(lenscorrection.DistortionCorrectionTask) ActionEvent(java.awt.event.ActionEvent) Bureaucrat(ini.trakem2.utils.Bureaucrat) ArrayList(java.util.ArrayList) List(java.util.List) CoordinateTransformList(mpicbg.trakem2.transform.CoordinateTransformList) HashSet(java.util.HashSet) Color(java.awt.Color) PolygonRoi(ij.gui.PolygonRoi) ShapeRoi(ij.gui.ShapeRoi) Roi(ij.gui.Roi) ActionListener(java.awt.event.ActionListener) DBObject(ini.trakem2.persistence.DBObject) InspectPatchTrianglesMode(ini.trakem2.display.inspect.InspectPatchTrianglesMode) GenericDialog(ij.gui.GenericDialog) Worker(ini.trakem2.utils.Worker) JMenuItem(javax.swing.JMenuItem) Point(java.awt.Point) TreeMap(java.util.TreeMap) JPopupMenu(javax.swing.JPopupMenu) Point(java.awt.Point) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) JMenu(javax.swing.JMenu)

Example 27 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class Display method importImage.

protected Bureaucrat importImage() {
    final Worker worker = new // / all this verbosity is what happens when functions are not first class citizens. I could abstract it away by passing a string name "importImage" and invoking it with reflection, but that is an even bigger PAIN
    Worker(// / all this verbosity is what happens when functions are not first class citizens. I could abstract it away by passing a string name "importImage" and invoking it with reflection, but that is an even bigger PAIN
    "Import image") {

        @Override
        public void run() {
            startedWorking();
            try {
                // /
                final Rectangle srcRect = canvas.getSrcRect();
                final int x = srcRect.x + srcRect.width / 2;
                final int y = srcRect.y + srcRect.height / 2;
                final Patch p = project.getLoader().importImage(project, x, y);
                if (null == p) {
                    finishedWorking();
                    Utils.showMessage("Could not open the image.");
                    return;
                }
                Display.this.getLayerSet().addLayerContentStep(layer);
                // will add it to the proper Displays
                layer.add(p);
                Display.this.getLayerSet().addLayerContentStep(layer);
            // /
            } catch (final Exception e) {
                IJError.print(e);
            }
            finishedWorking();
        }
    };
    return Bureaucrat.createAndStart(worker, getProject());
}
Also used : Rectangle(java.awt.Rectangle) Worker(ini.trakem2.utils.Worker) Point(java.awt.Point) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException)

Example 28 with Project

use of ini.trakem2.Project 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 29 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class Display method openLater.

/**
 * After reloading a project from the database, open the Displays that the project had.
 */
public static Bureaucrat openLater() {
    final HashMap<Display, Object[]> ht_later_local;
    final Project[] ps;
    synchronized (ht_later) {
        if (0 == ht_later.size())
            return null;
        ht_later_local = new HashMap<Display, Object[]>(ht_later);
        ht_later.keySet().removeAll(ht_later_local.keySet());
        final HashSet<Project> unique = new HashSet<Project>();
        for (final Display d : ht_later_local.keySet()) {
            unique.add(d.project);
        }
        ps = unique.toArray(new Project[unique.size()]);
    }
    final Worker worker = new Worker.Task("Opening displays") {

        @Override
        public void exec() {
            try {
                // waiting for Swing
                Thread.sleep(300);
                for (final Display d : ht_later_local.keySet()) {
                    // must be set as front before repainting any ZDisplayable!
                    addDisplay(d);
                    final Object[] props = ht_later_local.get(d);
                    if (ControlWindow.isGUIEnabled())
                        d.makeGUI(d.layer, props);
                    // important to do it after makeGUI
                    d.setLayerLater(d.layer, d.layer.get(((Long) props[3]).longValue()));
                    if (!ControlWindow.isGUIEnabled())
                        continue;
                    d.updateFrameTitle(d.layer);
                    // force a repaint if a prePaint was done TODO this should be properly managed with repaints using always the invokeLater, but then it's DOG SLOW
                    if (d.canvas.getMagnification() > 0.499) {
                        Utils.invokeLater(new Runnable() {

                            @Override
                            public void run() {
                                Display.repaint(d.layer);
                                d.project.getLoader().setChanged(false);
                                Utils.log2("A set to false");
                            }
                        });
                    }
                    d.project.getLoader().setChanged(false);
                    Utils.log2("B set to false");
                }
                if (null != front)
                    front.getProject().select(front.layer);
            } catch (final Throwable t) {
                IJError.print(t);
            }
        }
    };
    return Bureaucrat.createAndStart(worker, ps);
}
Also used : AlignTask(mpicbg.trakem2.align.AlignTask) AlignLayersTask(mpicbg.trakem2.align.AlignLayersTask) DistortionCorrectionTask(lenscorrection.DistortionCorrectionTask) Project(ini.trakem2.Project) Worker(ini.trakem2.utils.Worker) DBObject(ini.trakem2.persistence.DBObject) HashSet(java.util.HashSet)

Example 30 with Project

use of ini.trakem2.Project 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)

Aggregations

ArrayList (java.util.ArrayList)33 Project (ini.trakem2.Project)26 HashMap (java.util.HashMap)25 Layer (ini.trakem2.display.Layer)21 Displayable (ini.trakem2.display.Displayable)19 Patch (ini.trakem2.display.Patch)18 File (java.io.File)18 HashSet (java.util.HashSet)18 ZDisplayable (ini.trakem2.display.ZDisplayable)17 ImagePlus (ij.ImagePlus)16 ProjectThing (ini.trakem2.tree.ProjectThing)16 Worker (ini.trakem2.utils.Worker)16 TemplateThing (ini.trakem2.tree.TemplateThing)15 Map (java.util.Map)15 LayerSet (ini.trakem2.display.LayerSet)14 ResultSet (java.sql.ResultSet)13 DBObject (ini.trakem2.persistence.DBObject)12 AffineTransform (java.awt.geom.AffineTransform)11 TreeMap (java.util.TreeMap)11 FSLoader (ini.trakem2.persistence.FSLoader)10