net.sf.nmedit.jtheme.component.JTModuleContainer.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.nmedit.jtheme.component.JTModuleContainer.java

Source

/* Copyright (C) 2006 Christian Schneider
 * 
 * This file is part of Nomad.
 * 
 * Nomad is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * Nomad is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Nomad; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

/*
 * Created on Jan 21, 2007
 */
package net.sf.nmedit.jtheme.component;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.border.Border;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.nmedit.jpatch.CopyOperation;
import net.sf.nmedit.jpatch.InvalidDescriptorException;
import net.sf.nmedit.jpatch.ModuleDescriptions;
import net.sf.nmedit.jpatch.MoveOperation;
import net.sf.nmedit.jpatch.PConnection;
import net.sf.nmedit.jpatch.PConnectionManager;
import net.sf.nmedit.jpatch.PConnector;
import net.sf.nmedit.jpatch.PModule;
import net.sf.nmedit.jpatch.PModuleContainer;
import net.sf.nmedit.jpatch.PModuleDescriptor;
import net.sf.nmedit.jpatch.PModuleMetrics;
import net.sf.nmedit.jpatch.PPatch;
import net.sf.nmedit.jpatch.dnd.PDragDrop;
import net.sf.nmedit.jpatch.dnd.PModuleTransferData;
import net.sf.nmedit.jpatch.event.PConnectionEvent;
import net.sf.nmedit.jpatch.event.PConnectionListener;
import net.sf.nmedit.jpatch.event.PModuleContainerEvent;
import net.sf.nmedit.jpatch.event.PModuleContainerListener;
import net.sf.nmedit.jpatch.history.PUndoableEditSupport;
import net.sf.nmedit.jpatch.util.DescriptorNameComparator;
import net.sf.nmedit.jtheme.JTContext;
import net.sf.nmedit.jtheme.JTException;
import net.sf.nmedit.jtheme.cable.Cable;
import net.sf.nmedit.jtheme.cable.JTCableManager;
import net.sf.nmedit.jtheme.component.plaf.mcui.InsertModuleAction;
import net.sf.nmedit.jtheme.component.plaf.mcui.JTModuleContainerUI;
import net.sf.nmedit.jtheme.help.HelpHandler;
import net.sf.nmedit.jtheme.store2.ModuleElement;
import net.sf.nmedit.nmutils.dnd.FileDnd;
import net.sf.nmedit.nmutils.swing.NmSwingUtilities;

public class JTModuleContainer extends JTBaseComponent {

    private static final Log log = LogFactory.getLog(JTModuleContainer.class);

    /**
     * 
     */
    private static final long serialVersionUID = 4287282305262880549L;

    public static final String uiClassId = "ModuleContainerUI";

    private boolean optimizedDrawing;
    private JTPatch patchContainer;
    private PModuleContainer moduleContainer;
    private ContentSynchronisation cs;
    //private CableOverlay overlay;

    private JTLayerRoot cableLayer;

    public JTModuleContainer(JTContext context, JTCableManager cableManager) {
        super(context);
        setOpaque(true);
        setFocusable(true);
        optimizedDrawing = false;// 'true' does not work: !context.hasModuleContainerOverlay();
                                 // we have to overwrite boolean isPaintingOrigin() which is package private

        this.cableLayer = new JTLayerRoot(context);
        add(cableLayer);

        setCableManager(cableManager);
        cs = createContentSynchronisation();
        if (cs != null)
            cs.install();

        setJTFlag(FLAG_INVALIDATE, true);
        setJTFlag(FLAG_VALIDATE, true);
        setJTFlag(FLAG_REVALIDATE, true);
        setJTFlag(FLAG_VALIDATE_TREE, true);

        setLayout(new ModuleContainerLayout());
    }

    public HelpHandler getHelpHandler() {
        return patchContainer != null ? patchContainer.getHelpHandler() : null;
    }

    protected boolean isRepaintOrigin() {
        return true;
    }

    /**
     * Returns a mutable list of all modules in this container.
     * @return modules in this container
     */
    public List<JTModule> getModules() {
        return getComponents(JTModule.class);
    }

    private Set<JTModule> selectionSet = new HashSet<JTModule>();

    public int getSelectionSize() {
        return selectionSet.size();
    }

    public JTLayerRoot getLayerRoot() {
        return cableLayer;
    }

    public boolean isOnlyThisSelected(JTModule module) {
        return getSelectionSize() == 1 && isInSelection(module);
    }

    public void selectOnly(JTModule module) {
        if (!isOnlyThisSelected(module)) {
            if (!selectionSet.isEmpty()) {
                for (JTModule m : selectionSet)
                    m.setSelected(false);
                selectionSet.clear();
            }
            addSelection(module);
        }
    }

    public void addSelection(JTModule module) {
        JTPatch patch = getPatchContainer();
        for (JTModuleContainer c : patch.getModuleContainers()) {
            if (c != null && c != this) {
                c.clearSelection();
            }
        }
        selectionSet.add(module);
        module.setSelected(true);
    }

    public void removeSelection(JTModule module) {
        selectionSet.remove(module);
        module.setSelected(false);
    }

    public boolean isInSelection(JTModule module) {
        return selectionSet.contains(module);
    }

    public boolean isSelectionEmpty() {
        return selectionSet.isEmpty();
    }

    public Collection<? extends JTModule> getSelectedModules() {
        return new HashSet<JTModule>(selectionSet);
    }

    public Collection<? extends PModule> getSelectedPModules() {
        HashSet<PModule> result = new HashSet<PModule>();
        for (JTModule m : selectionSet) {
            result.add(m.getModule());

        }
        return result;
    }

    public void clearSelection() {
        if (selectionSet.isEmpty())
            return;

        for (JTModule module : selectionSet)
            module.setSelected(false);

        selectionSet.clear();
    }

    private JTCableManager cableManager;

    /*
        public Component findComponentAt(int x, int y) { 
    if (!(contains(x, y) && isVisible() && isEnabled())) {
        return null;
    }
    synchronized (getTreeLock())
    {
        for (int i=getComponentCount()-1;i>=0;i--)
        {
            Component comp = getComponent(i);
            if (comp != overlay)
            {
                if (comp instanceof Container)
                {
                    comp = ((Container)comp).findComponentAt(x-comp.getX(), y-comp.getY());
                }
                else
                {
                    comp = comp.getComponentAt(x, y);
                }
                if (comp != null && comp.isVisible() && comp.isEnabled())
                {
                    return comp;
                }
            }
        }
    }
    return this;
        }*/

    protected ContentSynchronisation createContentSynchronisation() {
        return new ContentSynchronisation();
    }

    protected class ContentSynchronisation implements PModuleContainerListener, PConnectionListener {
        private PModuleContainer mc;
        private PConnectionManager pc;
        //private boolean oneUpdate = false;

        public void install() {
            // no op
        }

        public void update() {
            setInstalledContainer(moduleContainer);
        }

        private void setInstalledContainer(PModuleContainer moduleContainer) {
            if (this.mc != null)
                uninstall(this.mc);
            this.mc = moduleContainer;
            if (this.mc != null)
                install(this.mc);
        }

        protected void install(PModuleContainer mc) {
            mc.addModuleContainerListener(this);
            pc = mc.getConnectionManager();
            if (pc != null)
                pc.addConnectionListener(this);
        }

        protected void uninstall(PModuleContainer mc) {
            mc.removeModuleContainerListener(this);
            if (pc != null)
                pc.removeConnectionListener(this);
            pc = null;
        }

        public void moduleAdded(PModuleContainerEvent e) {
            createUIFor(e.getModule());
        }

        public void moduleRemoved(PModuleContainerEvent e) {
            removeUI(e.getModule());
        }

        private void createUIFor(PModule module) {
            ModuleElement ms = getContext().getStorageContext().getModuleStoreById(module.getComponentId());

            try {
                JTModule mui = ms.createModule(getContext(), module);
                mui.setLocation(module.getScreenLocation());
                add(mui);
                JTModuleContainer.this.revalidate();
                mui.repaint();
            } catch (JTException e) {
                if (log.isErrorEnabled()) {
                    log.error("createUIFor(PModule) failed for module: " + module, e);
                }
            }
        }

        public void removeUI(PModule module) {
            for (JTModule mui : JTModuleContainer.this.getModules()) {
                if (mui.getModule() == module) {
                    int x = mui.getX();
                    int y = mui.getY();
                    int w = mui.getWidth();
                    int h = mui.getHeight();

                    remove(mui);
                    JTModuleContainer.this.revalidate();
                    JTModuleContainer.this.repaint(x, y, w, h);
                    break;
                }
            }
        }

        public void connectionAdded(PConnectionEvent e) {
            colorCables(e, true);
        }

        public void connectionRemoved(PConnectionEvent e) {
            colorCables(e, false);
        }

        public void colorCables(PConnectionEvent e, boolean joined) {
            if (joined) {
                Color color = null;
                PConnector graph = null;
                if (e.getDestination().getOutputConnector() != null) {
                    graph = e.getSource();
                    color = e.getDestination().getOutputConnector().getSignalType().getColor();

                } else if (e.getSource().getOutputConnector() != null) {
                    graph = e.getDestination();
                    color = e.getSource().getOutputConnector().getSignalType().getColor();
                }

                if (graph != null) {

                    Collection<Cable> cables = getCables(graph);
                    for (Cable cable : cables) {
                        cable.setColor(color);
                    }
                    getCableManager().update(cables);
                }
            } else {
                PConnector graph = null;
                if (e.getDestination().getOutputConnector() != null) {
                    graph = e.getSource();
                } else if (e.getSource().getOutputConnector() != null) {
                    graph = e.getDestination();
                }

                // make grey
                if (graph != null) {
                    Collection<Cable> cables = getCables(graph);
                    for (Cable cable : cables) {
                        // TODO lookup correct color in patch 
                        cable.setColor(Color.WHITE);
                    }
                    getCableManager().update(cables);
                }
            }
        }

        private Collection<Cable> getCables(PConnector c) {
            Collection<Cable> cables = new ArrayList<Cable>(getCableManager().size());
            Collection<PConnection> connections = c.getGraphConnections();

            for (Cable cable : getCableManager()) {
                for (Iterator<PConnection> iter = connections.iterator(); iter.hasNext();) {
                    if (isSameConnection(iter.next(), cable))
                        cables.add(cable);
                }
            }
            return cables;
        }

        private boolean isSameConnection(PConnection c, Cable cable) {
            // compare connection ((a1, b1) with (a2, b2)) or ((a1, b1) with (b2, a2)) 
            return isSameConnection(c.getA(), c.getB(), cable.getSource(), cable.getDestination())
                    || isSameConnection(c.getA(), c.getB(), cable.getDestination(), cable.getSource());
        }

        private boolean isSameConnection(PConnector a1, PConnector b1, PConnector a2, PConnector b2) {
            // compare connection (a1, b1) with (a2, b2)
            return a1 == a2 && b1 == b2;
        }

    }

    public void setModuleContainer(PModuleContainer mc) {
        this.moduleContainer = mc;
        setName(moduleContainer == null ? null : moduleContainer.getName());
        PModuleMetrics metrics = null;
        if (mc != null)
            metrics = mc.getModuleMetrics();
        if (metrics == null)
            setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
        else
            setMaximumSize(new Dimension(metrics.getMaxScreenX(), metrics.getMaxScreenY()));

        if (cs != null)
            cs.update();
    }

    public PModuleContainer getModuleContainer() {
        return moduleContainer;
    }

    public JTPatch getPatchContainer() {
        return patchContainer;
    }

    public void setPatchContainer(JTPatch patchContainer) {
        this.patchContainer = patchContainer;
    }

    public JTModuleContainerUI getUI() {
        return (JTModuleContainerUI) ui;
    }

    public void setUI(JTModuleContainerUI ui) {
        super.setUI(ui);
    }

    public String getUIClassID() {
        return uiClassId;
    }

    protected void setCableManager(JTCableManager cableManager) {
        this.cableManager = cableManager;
        cableLayer.setCableManager(cableManager);
    }

    public JTCableManager getCableManager() {
        return cableManager;
    }

    public boolean isOptimizedDrawingEnabled() {
        return optimizedDrawing;
    }

    /**
     * Calls {@link #paint(Graphics)}.
     */
    public void update(Graphics g) {
        paint(g);
    }

    public void setBorder(Border border) {
        if (border != null)
            throw new UnsupportedOperationException("border not supported");
    }

    protected final void paintBorder(Graphics g) {
        // no op
    }

    public boolean isDnDAllowed() {
        return getContext().isDnDAllowed();
    }

    private class ModuleContainerLayout implements LayoutManager2 {

        private boolean valid = false;
        private Dimension cachedSize = new Dimension();
        private Insets cachedInsets = new Insets(0, 0, 0, 0);

        public void invalidateLayout(Container target) {
            valid = false;
        }

        public void layoutContainer(Container parent) {
            JTModuleContainer mc = JTModuleContainer.this;
            // only layout cable layer
            JTLayerRoot cl = mc.cableLayer;
            if (cl != null) {
                synchronized (mc.getTreeLock()) {
                    Insets insets = mc.getInsets();
                    cl.setBounds(0, 0, mc.getWidth() - insets.left - insets.right,
                            mc.getHeight() - insets.top - insets.bottom);
                }
            }
        }

        public Dimension minimumLayoutSize(Container parent) {
            if (valid)
                return new Dimension(cachedSize);

            Dimension dim = new Dimension(0, 0);
            JTModuleContainer mc = JTModuleContainer.this;
            synchronized (mc.getTreeLock()) {
                for (int i = mc.getComponentCount() - 1; i >= 0; i--) {
                    Component c = mc.getComponent(i);
                    if (mc.cableLayer == c)
                        continue;

                    dim.width = Math.max(dim.width, c.getX() + c.getWidth());
                    dim.height = Math.max(dim.height, c.getY() + c.getHeight());
                }
            }

            boolean keepSize = false;

            JTLayerRoot cl = mc.cableLayer;
            if (cl != null) {
                final int EXTRA = 200;
                // also search through layers
                synchronized (cl.getTreeLock()) {
                    for (int i = cl.getComponentCount() - 1; i >= 0; i--) {
                        Component c = cl.getComponent(i);
                        dim.width = Math.max(dim.width, c.getX() + c.getWidth() + EXTRA);
                        dim.height = Math.max(dim.height, c.getY() + c.getHeight() + EXTRA);
                        keepSize = true;
                    }
                }
            }
            Insets insets = mc.getInsets(cachedInsets);
            dim.width += insets.left + insets.right;
            dim.height += insets.top + insets.bottom;

            if (keepSize) {
                cachedSize.width = Math.max(cachedSize.width, dim.width);
                cachedSize.height = Math.max(cachedSize.height, dim.height);
            } else {
                cachedSize.setSize(dim);
            }
            valid = true;
            return dim;
        }

        public Dimension maximumLayoutSize(Container target) {
            return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
        }

        public Dimension preferredLayoutSize(Container parent) {
            return minimumLayoutSize(parent);
        }

        public float getLayoutAlignmentX(Container target) {
            return 0;
        }

        public float getLayoutAlignmentY(Container target) {
            return 0;
        }

        public void addLayoutComponent(String name, Component comp) {
            // no op
        }

        public void addLayoutComponent(Component comp, Object constraints) {
            // no op
        }

        public void removeLayoutComponent(Component comp) {
            // no op
        }

    }

    public boolean dropPatch(PPatch newPatch, Point location) {
        PModuleContainer newMc = null;

        for (int i = 0; i < newPatch.getModuleContainerCount(); i++) {
            newMc = newPatch.getModuleContainer(i);
            if (newMc.getModuleCount() > 0)
                break;
            else
                newMc = null;
        }

        if (newMc == null) {
            return false;
        }
        CopyOperation op = newMc.createCopyOperation();
        op.setDestination(getModuleContainer());
        for (PModule module : newMc) {
            op.add(module);
        }
        op.setScreenOffset(location.x, location.y);
        op.copy();

        return true;
    }

    public void dropPatchFile(DropTargetDropEvent dtde) {
        Transferable transfer = dtde.getTransferable();
        PPatch patch = getModuleContainer().getPatch();
        DataFlavor fileFlavor = FileDnd.getFileFlavor(transfer.getTransferDataFlavors());
        List<File> files = FileDnd.getTransferableFiles(fileFlavor, transfer);
        if (files.size() == 1) {
            PPatch newPatch;
            try {
                newPatch = patch.createFromFile(files.get(0));
            } catch (Exception e) {
                newPatch = null;
            }
            if (newPatch != null) {
                if (dropPatch(newPatch, dtde.getLocation())) {
                    dtde.dropComplete(true);
                } else {
                    dtde.rejectDrop();
                    dtde.dropComplete(false);
                }
            } else {
                dtde.rejectDrop();
                dtde.dropComplete(false);
            }
        } else {
            dtde.rejectDrop();
            dtde.dropComplete(false);
        }
    }

    public void copyMoveModules(DropTargetDropEvent dtde) {
        DataFlavor chosen = PDragDrop.ModuleSelectionFlavor;
        Transferable transfer = dtde.getTransferable();
        Object data = null;

        try {
            // Get the data
            data = transfer.getTransferData(chosen);

            dtde.acceptDrop(dtde.getDropAction()
                    & (DnDConstants.ACTION_MOVE | DnDConstants.ACTION_COPY | DnDConstants.ACTION_LINK));
        } catch (Throwable t) {
            if (log.isWarnEnabled()) {
                log.warn("copyMoveModules(DropTargetDropEvent=" + dtde + ") failed", t);
            }
            dtde.dropComplete(false);
            return;
        }

        if (data != null && data instanceof PModuleTransferData) {
            // Cast the data and create a nice module.
            PModuleTransferData tdata = ((PModuleTransferData) data);
            boolean isSamePatch = false;
            if (tdata.getSourcePatch() == getModuleContainer().getPatch())
                isSamePatch = true;

            //Point p = dtde.getLocation();

            int action = dtde.getDropAction();

            if ((action & DnDConstants.ACTION_MOVE) != 0 && isSamePatch) {
                MoveOperation op = tdata.getSourceModuleContainer().createMoveOperation();
                op.setDestination(getModuleContainer());
                executeOperationOnSelection(tdata, dtde.getLocation(), op);
            } else {
                copyModules(tdata, dtde.getLocation(), ((dtde.getDropAction() & DnDConstants.ACTION_LINK) != 0));
            }

        }
        dtde.dropComplete(true);
    }

    public void copyModules(PModuleTransferData tdata, Point p, boolean link) {
        if (tdata != null && tdata.getSourceModuleContainer() != null) {
            CopyOperation op = tdata.getSourceModuleContainer().createCopyOperation();
            // check for shift pressed to create links XXX
            if (tdata.getSourcePatch() == getPatchContainer().getPatch() && link) {
                op.setDuplicate(true);
            }
            op.setDestination(getModuleContainer());
            executeOperationOnSelection(tdata, p, op);
        }
    }

    public void dropNewModule(DropTargetDropEvent dtde) {
        PModuleContainer mc = getModuleContainer();
        PModuleDescriptor md = PDragDrop.getModuleDescriptor(dtde.getTransferable());
        if (md == null || mc == null) {
            dtde.rejectDrop();
            return;
        }

        Point l = dtde.getLocation();

        PModule module;
        try {
            module = mc.createModule(md);
            module.setScreenLocation(l.x, l.y);
        } catch (InvalidDescriptorException e) {
            if (log.isErrorEnabled()) {
                log.error("could not create module: " + md, e);
            }
            dtde.rejectDrop();
            return;
        }
        boolean moduleAdded = mc.add(module);

        if (!moduleAdded) {

            dtde.rejectDrop();
            return;
        }

        // TODO short after dropping a new module and then moving it
        // causes a NullPointerException in the next line
        PModuleContainer parent = module.getParentComponent();
        if (parent != null) {
            JTCableManager cm = getCableManager();
            try {
                cm.setAutoRepaintDisabled();
                MoveOperation move = parent.createMoveOperation();
                move.setScreenOffset(0, 0);
                move.add(module);
                move.move();
            } finally {
                cm.clearAutoRepaintDisabled();
            }
        } else {
            // XXX concurrency problems probably ?!
            throw new RuntimeException("Drop problem on illegal modules: for example 2 midi globals");
        }

        dtde.acceptDrop(DnDConstants.ACTION_COPY);

        // compute dimensions of container
        revalidate();
        repaint();
        dtde.dropComplete(true);
    }

    public void executeOperationOnSelection(PModuleTransferData tdata, Point p, MoveOperation op) {
        Point o = tdata.getDragStartLocation();

        p.x = p.x - o.x;
        p.y = p.y - o.y;

        JTModuleContainer jtmc = this;
        PUndoableEditSupport ues = jtmc.getModuleContainer().getEditSupport();
        JTCableManager cm = jtmc.getCableManager();
        PModuleContainer mc = jtmc.getModuleContainer();

        for (PModule module : tdata.getModules()) {
            op.add(module);
        }

        op.setScreenOffset(p.x, p.y);

        try {
            cm.setAutoRepaintDisabled();
            String name = (op instanceof CopyOperation ? "copy modules" : "move modules");
            if (tdata.getModules().size() > 1)
                ues.beginUpdate(name);
            else
                ues.beginUpdate();
            try {
                op.move();
            } finally {
                ues.endUpdate();
            }

            Collection<? extends PModule> moved = op.getMovedModules();

            int maxx = 0;
            int maxy = 0;

            for (JTModule jtmodule : NmSwingUtilities.getChildren(JTModule.class, jtmc)) {
                PModule module = jtmodule.getModule();
                if (moved.contains(module)) {
                    jtmodule.setLocation(module.getScreenLocation());

                    maxx = Math.max(jtmodule.getX(), maxx) + jtmodule.getWidth();
                    maxy = Math.max(jtmodule.getY(), maxy) + jtmodule.getHeight();
                }
            }

            Collection<Cable> cables = new ArrayList<Cable>(20);
            cm.getCables(cables, moved);
            for (Cable cable : cables) {
                cm.update(cable);
                cable.updateEndPoints();
                cm.update(cable);
            }

            jtmc.revalidate();
            jtmc.repaint();
        } finally {
            cm.clearAutoRepaintDisabled();
        }
    }

    public void installModulesMenu(JPopupMenu menu) {
        PModuleContainer container = getModuleContainer();
        ModuleDescriptions modules = getModuleContainer().getPatch().getModuleDescriptions();
        Map<String, List<PModuleDescriptor>> categoryMap = new HashMap<String, List<PModuleDescriptor>>();

        for (PModuleDescriptor module : modules) {
            if (module.isInstanciable() && container.canAdd(module)) {
                String cat = module.getCategory();
                List<PModuleDescriptor> catList = categoryMap.get(cat);
                if (catList == null) {
                    catList = new ArrayList<PModuleDescriptor>();
                    categoryMap.put(cat, catList);
                }
                catList.add(module);
            }
        }

        Comparator<PModuleDescriptor> order = new DescriptorNameComparator<PModuleDescriptor>();
        List<String> categories = new ArrayList<String>();
        categories.addAll(categoryMap.keySet());
        Collections.sort(categories);

        for (String cat : categories) {
            List<PModuleDescriptor> catList = categoryMap.get(cat);
            Collections.sort(catList, order);

            JMenu catMenu = new JMenu(cat);
            for (PModuleDescriptor m : catList) {
                catMenu.add(new InsertModuleAction(m, this));
            }

            menu.add(catMenu);
        }
    }

}