All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cayenne.modeler.ProjectTreeView Maven / Gradle / Ivy

/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/

package org.apache.cayenne.modeler;

import org.apache.cayenne.configuration.ConfigurationNode;
import org.apache.cayenne.configuration.DataChannelDescriptor;
import org.apache.cayenne.configuration.DataNodeDescriptor;
import org.apache.cayenne.configuration.event.DataMapEvent;
import org.apache.cayenne.configuration.event.DataMapListener;
import org.apache.cayenne.configuration.event.DataNodeEvent;
import org.apache.cayenne.configuration.event.DataNodeListener;
import org.apache.cayenne.configuration.event.DomainEvent;
import org.apache.cayenne.configuration.event.DomainListener;
import org.apache.cayenne.configuration.event.ProcedureEvent;
import org.apache.cayenne.configuration.event.ProcedureListener;
import org.apache.cayenne.configuration.event.QueryEvent;
import org.apache.cayenne.configuration.event.QueryListener;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.Embeddable;
import org.apache.cayenne.map.Entity;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.Procedure;
import org.apache.cayenne.map.event.DbEntityListener;
import org.apache.cayenne.map.event.EmbeddableEvent;
import org.apache.cayenne.map.event.EmbeddableListener;
import org.apache.cayenne.map.event.EntityEvent;
import org.apache.cayenne.map.event.ObjEntityListener;
import org.apache.cayenne.modeler.action.CopyAction;
import org.apache.cayenne.modeler.action.CreateDataMapAction;
import org.apache.cayenne.modeler.action.CreateDbEntityAction;
import org.apache.cayenne.modeler.action.CreateEmbeddableAction;
import org.apache.cayenne.modeler.action.CreateNodeAction;
import org.apache.cayenne.modeler.action.CreateObjEntityAction;
import org.apache.cayenne.modeler.action.CreateProcedureAction;
import org.apache.cayenne.modeler.action.CreateQueryAction;
import org.apache.cayenne.modeler.action.CutAction;
import org.apache.cayenne.modeler.action.DbEntitySyncAction;
import org.apache.cayenne.modeler.action.LinkDataMapsAction;
import org.apache.cayenne.modeler.action.ObjEntitySyncAction;
import org.apache.cayenne.modeler.action.PasteAction;
import org.apache.cayenne.modeler.action.RemoveAction;
import org.apache.cayenne.modeler.event.DataMapDisplayEvent;
import org.apache.cayenne.modeler.event.DataMapDisplayListener;
import org.apache.cayenne.modeler.event.DataNodeDisplayEvent;
import org.apache.cayenne.modeler.event.DataNodeDisplayListener;
import org.apache.cayenne.modeler.event.DbEntityDisplayListener;
import org.apache.cayenne.modeler.event.DomainDisplayEvent;
import org.apache.cayenne.modeler.event.DomainDisplayListener;
import org.apache.cayenne.modeler.event.EmbeddableDisplayEvent;
import org.apache.cayenne.modeler.event.EmbeddableDisplayListener;
import org.apache.cayenne.modeler.event.EntityDisplayEvent;
import org.apache.cayenne.modeler.event.MultipleObjectsDisplayEvent;
import org.apache.cayenne.modeler.event.MultipleObjectsDisplayListener;
import org.apache.cayenne.modeler.event.ObjEntityDisplayListener;
import org.apache.cayenne.modeler.event.ProcedureDisplayEvent;
import org.apache.cayenne.modeler.event.ProcedureDisplayListener;
import org.apache.cayenne.modeler.event.QueryDisplayEvent;
import org.apache.cayenne.modeler.event.QueryDisplayListener;
import org.apache.cayenne.modeler.util.CayenneAction;
import org.apache.cayenne.modeler.util.CellRenderers;
import org.apache.cayenne.modeler.util.Comparators;
import org.apache.cayenne.project.Project;
import org.apache.cayenne.map.QueryDescriptor;
import org.apache.cayenne.reflect.PropertyUtils;
import org.apache.cayenne.resource.Resource;
import org.apache.cayenne.swing.components.TopBorder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.MenuElement;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.dnd.DnDConstants;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * Panel displaying Cayenne project as a tree.
 */
public class ProjectTreeView extends JTree implements DomainDisplayListener,
        DomainListener, DataMapDisplayListener, DataMapListener, DataNodeDisplayListener,
        DataNodeListener, ObjEntityListener, ObjEntityDisplayListener, DbEntityListener,
        DbEntityDisplayListener, QueryListener, QueryDisplayListener, ProcedureListener,
        ProcedureDisplayListener, MultipleObjectsDisplayListener,
        EmbeddableDisplayListener, EmbeddableListener {

    private static final Logger logObj = LoggerFactory.getLogger(ProjectTreeView.class);

    private static final Color SELECTION_COLOR = UIManager.getColor("Tree.selectionBackground");

    protected ProjectController mediator;
    protected TreeSelectionListener treeSelectionListener;
    protected TreeWillExpandListener treeWillExpandListener;
    protected JPopupMenu popup;
    private TreeDragSource tds;

    public ProjectTreeView(ProjectController mediator) {
        super();
        this.mediator = mediator;

        initView();
        initController();
        initFromModel(Application.getInstance().getProject());
        this.tds = new TreeDragSource(this, DnDConstants.ACTION_COPY, mediator);
    }

    private void initView() {
        setCellRenderer(CellRenderers.treeRenderer());
        setOpaque(false);
        setBorder(TopBorder.create());
    }

    private void initController() {
        initTreeSelectionListener();
        initTreeExpandListener();
        addMouseListener(new MouseClickHandler());
        setupMediator();
    }

	private void initTreeSelectionListener() {
		treeSelectionListener = new TreeSelectionListener() {

            public void valueChanged(TreeSelectionEvent e) {
                TreePath[] paths = getSelectionPaths();

                if (paths != null) {
                    if (paths.length > 1) {
                        ConfigurationNode projectParentPath = null;
                        ConfigurationNode[] projectPaths = new ConfigurationNode[paths.length];
                        boolean commonParentPath = true;

                        for (int i = 0; i < paths.length; i++) {
                            projectPaths[i] = createProjectPath(paths[i]);

                            TreePath parentPath = paths[i].getParentPath();
                            if(i>0 && parentPath != null && !parentPath.equals(paths[i - 1].getParentPath())) {
                                commonParentPath = false;
                            }
                        }

                        if(commonParentPath) {
                            TreePath parentPath = paths[0].getParentPath();
                            projectParentPath = createProjectPath(parentPath);
                        }

                        mediator.fireMultipleObjectsDisplayEvent(new MultipleObjectsDisplayEvent(
                                        this,
                                        projectPaths, projectParentPath));
                    } else if (paths.length == 1) {
                        processSelection(paths[0]);
                    }
                }
            }

            /**
             * Converts TreePath to Object
             */
            private ConfigurationNode createProjectPath(TreePath treePath) {
                Object[] path = treePath.getPath();
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)path[path.length - 1];
                return (ConfigurationNode) treeNode.getUserObject();
            }
        };

        addTreeSelectionListener(treeSelectionListener);
	}

	private void initTreeExpandListener() {
        treeWillExpandListener = new TreeWillExpandListener() {
            @Override
            public void treeWillExpand(TreeExpansionEvent e) {
                TreePath path = e.getPath();
                if (!isPathSelected(path) && !isSelectionEmpty()) {
                    setSelectionPath(path);
                }
            }

            @Override
            public void treeWillCollapse(TreeExpansionEvent e) {
                TreePath path = e.getPath();
                if (!isPathSelected(path) && !isSelectionEmpty()) {
                    setSelectionPath(path);
                }
            }
        };


        addTreeWillExpandListener(treeWillExpandListener);
    }

	private void setupMediator() {
		mediator.addDomainListener(this);
        mediator.addDomainDisplayListener(this);
        mediator.addDataNodeListener(this);
        mediator.addDataNodeDisplayListener(this);
        mediator.addDataMapListener(this);
        mediator.addDataMapDisplayListener(this);
        mediator.addObjEntityListener(this);
        mediator.addObjEntityDisplayListener(this);
        mediator.addDbEntityListener(this);
        mediator.addDbEntityDisplayListener(this);
        mediator.addEmbeddableDisplayListener(this);
        mediator.addEmbeddableListener(this);
        mediator.addProcedureListener(this);
        mediator.addProcedureDisplayListener(this);
        mediator.addQueryListener(this);
        mediator.addQueryDisplayListener(this);
        mediator.addMultipleObjectsDisplayListener(this);
        mediator.getApplication().getActionManager().setupCutCopyPaste(
                this,
                CutAction.class,
                CopyAction.class);
	}

    private void initFromModel(Project project) {

        // build model
        ProjectTreeModel model = new ProjectTreeModel(project);
        setRootVisible(true);
        setModel(model);
        getSelectionModel().setSelectionMode(
                TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
    }

    /**
     * Returns tree model cast to ProjectTreeModel.
     */
    public ProjectTreeModel getProjectModel() {
        return (ProjectTreeModel) getModel();
    }

    /**
     * Returns a "name" property of the tree node.
     */
    @Override
    public String convertValueToText(
            Object value,
            boolean selected,
            boolean expanded,
            boolean leaf,
            int row,
            boolean hasFocus) {

        // unwrap
        while (value instanceof DefaultMutableTreeNode) {
            value = ((DefaultMutableTreeNode) value).getUserObject();
        }

        // String - just return it
        if (value instanceof String) {
            return value.toString();
        }

        // Project - return the name of top file
        if (value instanceof Project) {
            Resource resource = ((Project) value).getConfigurationResource();
            return (resource != null) ? resource.getURL().getPath() : "";
        }

        // read name property
        try {
            if (value instanceof Embeddable) {
                return String.valueOf(PropertyUtils.getProperty(value, "className"));
            }

            return (value != null) ? String.valueOf(PropertyUtils.getProperty(value, "name")) : "";
        } catch (Exception e) {
            logObj.warn("Exception reading property 'name', class " + value.getClass().getName(), e);
            return "";
        }
    }

    public void currentDomainChanged(DomainDisplayEvent e) {
        if ((e.getSource() == this || !e.isDomainChanged()) && !e.isRefired()) {
            return;
        }

        showNode(new Object[] {
            e.getDomain()
        });
    }

    public void currentDataNodeChanged(DataNodeDisplayEvent e) {
        if ((e.getSource() == this || !e.isDataNodeChanged()) && !e.isRefired()) {
            return;
        }

        showNode(new Object[] {
                e.getDomain(), e.getDataNode()
        });
    }

    public void currentDataMapChanged(DataMapDisplayEvent e) {
        if ((e.getSource() == this || !e.isDataMapChanged()) && !e.isRefired()) {
            return;
        }

        showNode(new Object[] {
                e.getDomain(), e.getDataMap()
        });
    }

    public void currentObjEntityChanged(EntityDisplayEvent e) {
        e.setEntityChanged(true);
        currentEntityChanged(e);
    }

    public void currentDbEntityChanged(EntityDisplayEvent e) {
        e.setEntityChanged(true);
        currentEntityChanged(e);
    }

    protected void currentEntityChanged(EntityDisplayEvent e) {
        if ((e.getSource() == this || !e.isEntityChanged()) && !e.isRefired()) {
            return;
        }

        showNode(new Object[] {
                e.getDomain(), e.getDataMap(), e.getEntity()
        });
    }

    public void currentProcedureChanged(ProcedureDisplayEvent e) {
        if ((e.getSource() == this || !e.isProcedureChanged()) && !e.isRefired()) {
            return;
        }

        showNode(new Object[] {
                e.getDomain(), e.getDataMap(), e.getProcedure()
        });
    }

    public void currentQueryChanged(QueryDisplayEvent e) {
        if ((e.getSource() == this || !e.isQueryChanged()) && !e.isRefired()) {
            return;
        }

        showNode(new Object[] {
                e.getDomain(), e.getDataMap(), e.getQuery()
        });
    }

    public void currentObjectsChanged(MultipleObjectsDisplayEvent e, Application application) {
        if (e.getSource() == this || e.getParentNode() == null) {
            return;
        }

        ConfigurationNode[] nodes = e.getNodes();
        TreePath[] treePaths = new TreePath[nodes.length];

        for (int i = 0; i < nodes.length; i++) {
            DefaultMutableTreeNode treeNode = getProjectModel().getNodeForObjectPath(new Object[] {e.getParentNode(), nodes[i]});
            if (treeNode != null) {
                treePaths[i] = new TreePath(treeNode.getPath());
            } else if (e.getParentNode() == nodes[i]) {
                treeNode = getProjectModel().getNodeForObjectPath(new Object[] {e.getParentNode()});
                treePaths[i] = new TreePath(treeNode.getPath());
            }
        }

        if (!isVisible(treePaths[0])) {
            makeVisible(treePaths[0]);

            Rectangle bounds = getPathBounds(treePaths[0]);
            if (bounds != null) {
                bounds.height = getVisibleRect().height;
                scrollRectToVisible(bounds);
            }
        }

        setSelectionPaths(treePaths);
    }

    public void procedureAdded(ProcedureEvent e) {

        DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(
                new Object[] {
                        mediator.getProject().getRootNode(),
                        e.getProcedure().getDataMap()
                });

        if (node == null) {
            return;
        }

        Procedure procedure = e.getProcedure();
        DefaultMutableTreeNode currentNode = new DefaultMutableTreeNode(procedure, false);
        positionNode(node, currentNode, Comparators.getDataMapChildrenComparator());
        showNode(currentNode);
    }

    public void procedureChanged(ProcedureEvent e) {
        if (e.isNameChange()) {
            Object[] path = new Object[] {
                    mediator.getProject().getRootNode(),
                    e.getProcedure().getDataMap(), e.getProcedure()
            };

            updateNode(path);
            positionNode(path, Comparators.getDataMapChildrenComparator());
            showNode(path);
        }
    }

    public void procedureRemoved(ProcedureEvent e) {

        removeNode(new Object[] {
                mediator.getProject().getRootNode(),
                e.getProcedure().getDataMap(), e.getProcedure()
        });
    }

    public void queryAdded(QueryEvent e) {

        DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(
                new Object[] {
                        e.getDomain() != null
                                ? e.getDomain()
                                : (DataChannelDescriptor) mediator
                                        .getProject()
                                        .getRootNode(), e.getDataMap()
                });

        if (node == null) {
            return;
        }

        QueryDescriptor query = e.getQuery();
        DefaultMutableTreeNode currentNode = new DefaultMutableTreeNode(query, false);
        positionNode(node, currentNode, Comparators.getDataMapChildrenComparator());
        showNode(currentNode);
    }

    public void queryChanged(QueryEvent e) {

        if (e.isNameChange()) {
            Object[] path = new Object[] {
                    e.getDomain() != null
                            ? e.getDomain()
                            : (DataChannelDescriptor) mediator.getProject().getRootNode(),
                    e.getQuery().getDataMap(), e.getQuery()
            };

            updateNode(path);
            positionNode(path, Comparators.getDataMapChildrenComparator());
            showNode(path);
        }
    }

    public void queryRemoved(QueryEvent e) {
        removeNode(new Object[] {
                e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator
                        .getProject()
                        .getRootNode(), e.getDataMap(), e.getQuery()
        });
    }

    public void domainChanged(DomainEvent e) {

        Object[] path = new Object[] {
            e.getDomain()
        };

        updateNode(path);

        if (e.isNameChange()) {

            positionNode(path, Comparators.getNamedObjectComparator());
            showNode(path);
        }
    }

    public void dataNodeChanged(DataNodeEvent e) {

        DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(
                new Object[] {
                        e.getDomain() != null
                                ? e.getDomain()
                                : (DataChannelDescriptor) mediator
                                        .getProject()
                                        .getRootNode(), e.getDataNode()
                });

        if (node != null) {

            if (e.isNameChange()) {
                positionNode((DefaultMutableTreeNode) node.getParent(), node,
                        Comparators.getDataDomainChildrenComparator());
                showNode(node);
            } else {

                getProjectModel().nodeChanged(node);

                DataChannelDescriptor domain = (DataChannelDescriptor) mediator.getProject().getRootNode();

                // check for DataMap additions/removals...
                String[] mapsName = e.getDataNode().getDataMapNames().toArray(new String[0]);
                int mapCount = mapsName.length;

                // DataMap was linked
                if (mapCount > node.getChildCount()) {

                    for (String aMapsName : mapsName) {
                        boolean found = false;
                        for (int j = 0; j < node.getChildCount(); j++) {
                            DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(j);
                            if (domain.getDataMap(aMapsName) == child.getUserObject()) {
                                found = true;
                                break;
                            }
                        }

                        if (!found) {
                            DefaultMutableTreeNode newMapNode =
                                    new DefaultMutableTreeNode(domain.getDataMap(aMapsName), false);
                            positionNode(node, newMapNode, Comparators.getNamedObjectComparator());
                            break;
                        }
                    }
                } else if (mapCount < node.getChildCount()) {
                    // DataMap was unlinked
                    int j = 0;
                    while (j < node.getChildCount()) {
                        boolean found = false;
                        DefaultMutableTreeNode child;
                        child = (DefaultMutableTreeNode) node.getChildAt(j);
                        Object obj = child.getUserObject();
                        for (Object aMapsName : mapsName) {
                            if (domain.getDataMap(aMapsName.toString()) == obj) {
                                found = true;
                                j++;
                            }
                        }
                        if (!found) {
                            removeNode(child);
                        }
                    }
                }
            }
        }
    }

    public void dataNodeAdded(DataNodeEvent e) {
        if (e.getSource() == this) {
            return;
        }

        DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(
                new Object[] {
                    e.getDomain() != null
                            ? e.getDomain()
                            : (DataChannelDescriptor) mediator.getProject().getRootNode()
                });

        if (node == null) {
            return;
        }

        DataNodeDescriptor dataNode = e.getDataNode();
        DefaultMutableTreeNode currentNode = ProjectTreeFactory.wrapProjectNode(dataNode);
        positionNode(node, currentNode, Comparators.getDataDomainChildrenComparator());
        showNode(currentNode);
    }

    public void dataNodeRemoved(DataNodeEvent e) {
        if (e.getSource() == this) {
            return;
        }

        removeNode(new Object[] {
                e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator
                        .getProject()
                        .getRootNode(), e.getDataNode()
        });
    }

    public void dataMapChanged(DataMapEvent e) {

        Object[] path = new Object[] {
                e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator
                        .getProject()
                        .getRootNode(), e.getDataMap()
        };

        updateNode(path);

        if (e.isNameChange()) {
            mediator.updateEntityResolver();
            positionNode(path, Comparators.getDataDomainChildrenComparator());
            showNode(path);
        }
    }

    public void dataMapAdded(DataMapEvent e) {
        DataChannelDescriptor dataChannelDescriptor = e.getDomain() != null ? e.getDomain() :
                (DataChannelDescriptor) mediator.getProject().getRootNode();
        DefaultMutableTreeNode domainNode = getProjectModel().getNodeForObjectPath(
                new Object[] {
                   dataChannelDescriptor
                });

        DefaultMutableTreeNode newMapNode = ProjectTreeFactory.wrapProjectNode(e
                .getDataMap());

        mediator.getEntityResolver().addDataMap(e.getDataMap());

        positionNode(domainNode, newMapNode, Comparators
                .getDataDomainChildrenComparator());
        if(!Application.getInstance().getFrameController().getDbImportController().isGlobalImport()) {
            showNode(newMapNode);
        } else {
            setSelected(newMapNode);
        }

        for (DataNodeDescriptor dataNode : new ArrayList<>(dataChannelDescriptor.getNodeDescriptors())) {
            for(String dataMapName : dataNode.getDataMapNames()) {
                if(e.getDataMap().getName().equals(dataMapName)) {
                    mediator.fireDataNodeEvent(new DataNodeEvent(this, dataNode));
                }
            }
        }
    }

    public void dataMapRemoved(DataMapEvent e) {
        DataMap map = e.getDataMap();
        DataChannelDescriptor dataChannelDescriptor = (DataChannelDescriptor) Application
                .getInstance()
                .getProject()
                .getRootNode();
        removeNode(new Object[] {
                dataChannelDescriptor, map
        });

        mediator.getEntityResolver().removeDataMap(e.getDataMap());

        // Clean up map from the nodes
        for (DataNodeDescriptor dataNode : new ArrayList<>(dataChannelDescriptor.getNodeDescriptors())) {
            removeNode(new Object[] {
                    dataChannelDescriptor, dataNode, map
            });
        }
    }

    public void objEntityChanged(EntityEvent e) {
        entityChanged(e);
    }

    public void objEntityAdded(EntityEvent e) {
        entityAdded(e);
    }

    public void objEntityRemoved(EntityEvent e) {
        entityRemoved(e);
    }

    public void dbEntityChanged(EntityEvent e) {
        entityChanged(e);
    }

    public void dbEntityAdded(EntityEvent e) {
        entityAdded(e);
    }

    public void dbEntityRemoved(EntityEvent e) {
        entityRemoved(e);
    }

    /**
     * Makes Entity visible and selected.
     * 
    *
  • If entity is from the current node, refreshes the node making sure changes in * the entity name are reflected.
  • *
  • If entity is in a different node, makes that node visible and selected.
  • *
*/ protected void entityChanged(EntityEvent e) { if (e.isNameChange()) { Object[] path = new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator.getProject().getRootNode(), e.getEntity().getDataMap(), e.getEntity() }; updateNode(path); positionNode(path, Comparators.getDataMapChildrenComparator()); showNode(path); } } /** * Event handler for ObjEntity and DbEntity additions. Adds a tree node for the entity * and make it selected. */ protected void entityAdded(EntityEvent e) { Entity entity = e.getEntity(); DefaultMutableTreeNode mapNode = getProjectModel().getNodeForObjectPath( new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator .getProject() .getRootNode(), e.getEntity().getDataMap() }); if (mapNode == null) { return; } DefaultMutableTreeNode currentNode = new DefaultMutableTreeNode(entity, false); positionNode(mapNode, currentNode, Comparators.getDataMapChildrenComparator()); showNode(currentNode); } /** * Event handler for ObjEntity and DbEntity removals. Removes a tree node for the * entity and selects its sibling. */ protected void entityRemoved(EntityEvent e) { if (e.getSource() == this) { return; } // remove from DataMap tree removeNode(new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator .getProject() .getRootNode(), e.getEntity().getDataMap(), e.getEntity() }); } /** * Removes current node from the tree. Selects a new node adjacent to the currently * selected node instead. */ protected void removeNode(DefaultMutableTreeNode toBeRemoved) { // lookup for the new selected node Object selectedNode = null; TreePath selectionPath = getSelectionPath(); if (selectionPath != null) { selectedNode = selectionPath.getLastPathComponent(); } if (toBeRemoved == selectedNode) { // first search siblings DefaultMutableTreeNode newSelection = toBeRemoved.getNextSibling(); if (newSelection == null) { newSelection = toBeRemoved.getPreviousSibling(); // try parent if (newSelection == null) { newSelection = (DefaultMutableTreeNode) toBeRemoved.getParent(); // search the whole tree if (newSelection == null) { newSelection = toBeRemoved.getNextNode(); if (newSelection == null) { newSelection = toBeRemoved.getPreviousNode(); } } } } showNode(newSelection); } // remove this node getProjectModel().removeNodeFromParent(toBeRemoved); } /** Makes node current, visible and selected. */ protected void showNode(DefaultMutableTreeNode node) { TreePath path = new TreePath(node.getPath()); if (!isVisible(path)) { makeVisible(path); Rectangle bounds = getPathBounds(path); if (bounds != null) { bounds.height = getVisibleRect().height; scrollRectToVisible(bounds); } } setSelectionPath(path); } /** Makes node current, visible but not selected. */ private void setSelected(DefaultMutableTreeNode node) { TreePath path = new TreePath(node.getPath()); if(!isVisible(path)) { makeVisible(path); } } protected void showNode(Object[] path) { if (path == null) { return; } DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(path); if (node == null) { return; } this.showNode(node); } protected void updateNode(Object[] path) { if (path == null) { return; } DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(path); if (node != null) { getProjectModel().nodeChanged(node); } } protected void removeNode(Object[] path) { if (path == null) { return; } DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(path); if (node != null) { removeNode(node); } } /** * Processes node selection regardless of whether a new node was selected, or an * already selected node was clicked again. Normally called from event listener * methods. */ public void processSelection(TreePath path) { if (path == null) { return; } DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) path.getLastPathComponent(); Object[] data = getUserObjects(currentNode); if (data.length == 0) { // this should clear the right-side panel DomainDisplayEvent domEvent = new DomainDisplayEvent(this, null); domEvent.setDomain((DataChannelDescriptor) mediator .getProject() .getRootNode()); mediator.fireDomainDisplayEvent(domEvent); return; } Object obj = data[data.length - 1]; if (obj instanceof DataChannelDescriptor) { mediator.fireDomainDisplayEvent(new DomainDisplayEvent( this, (DataChannelDescriptor) obj)); } else if (obj instanceof DataMap) { if (data.length == 2) { mediator.fireDataMapDisplayEvent(new DataMapDisplayEvent( this, (DataMap) obj, (DataChannelDescriptor) mediator.getProject().getRootNode(), (DataNodeDescriptor) data[data.length - 2])); } else if (data.length == 1) { mediator.fireDataMapDisplayEvent(new DataMapDisplayEvent( this, (DataMap) obj, (DataChannelDescriptor) mediator.getProject().getRootNode())); } } else if (obj instanceof DataNodeDescriptor) { if (data.length == 1) { mediator.fireDataNodeDisplayEvent(new DataNodeDisplayEvent( this, (DataChannelDescriptor) mediator.getProject().getRootNode(), (DataNodeDescriptor) obj)); } } else if (obj instanceof Entity) { EntityDisplayEvent e = new EntityDisplayEvent(this, (Entity) obj); e.setUnselectAttributes(true); if (data.length == 3) { e.setDataMap((DataMap) data[data.length - 2]); e.setDomain((DataChannelDescriptor) mediator.getProject().getRootNode()); e.setDataNode((DataNodeDescriptor) data[data.length - 3]); } else if (data.length == 2) { e.setDataMap((DataMap) data[data.length - 2]); e.setDomain((DataChannelDescriptor) mediator.getProject().getRootNode()); } if (obj instanceof ObjEntity) { mediator.fireObjEntityDisplayEvent(e); } else if (obj instanceof DbEntity) { mediator.fireDbEntityDisplayEvent(e); } } else if (obj instanceof Embeddable) { EmbeddableDisplayEvent e = new EmbeddableDisplayEvent( this, (Embeddable) obj, (DataMap) data[data.length - 2], (DataChannelDescriptor) mediator.getProject().getRootNode()); mediator.fireEmbeddableDisplayEvent(e); } else if (obj instanceof Procedure) { ProcedureDisplayEvent e = new ProcedureDisplayEvent( this, (Procedure) obj, (DataMap) data[data.length - 2], (DataChannelDescriptor) mediator.getProject().getRootNode()); mediator.fireProcedureDisplayEvent(e); } else if (obj instanceof QueryDescriptor) { QueryDisplayEvent e = new QueryDisplayEvent( this, (QueryDescriptor) obj, (DataMap) data[data.length - 2], (DataChannelDescriptor) mediator.getProject().getRootNode()); mediator.fireQueryDisplayEvent(e); } this.scrollPathToVisible(path); } /** * Returns array of the user objects ending with this and starting with one under * root. That is the array of actual objects rather than wrappers. */ private Object[] getUserObjects(DefaultMutableTreeNode node) { List list = new ArrayList<>(); while (!node.isRoot()) { list.add(0, node.getUserObject()); node = (DefaultMutableTreeNode) node.getParent(); } return list.toArray(); } private void positionNode(Object[] path, Comparator comparator) { if (path == null) { return; } DefaultMutableTreeNode node = getProjectModel().getNodeForObjectPath(path); if (node == null) { return; } positionNode(null, node, comparator); } private void positionNode( MutableTreeNode parent, DefaultMutableTreeNode treeNode, Comparator comparator) { removeTreeSelectionListener(treeSelectionListener); try { getProjectModel().positionNode(parent, treeNode, comparator); } finally { addTreeSelectionListener(treeSelectionListener); } } public TreeSelectionListener getTreeSelectionListener() { return treeSelectionListener; } /** * Creates JPopupMenu containing main functions */ private JPopupMenu createJPopupMenu() { JPopupMenu popup = new JPopupMenu(); popup.add(buildMenu(CreateNodeAction.class)); popup.add(buildMenu(CreateDataMapAction.class)); popup.add(buildMenu(CreateObjEntityAction.class)); popup.add(buildMenu(CreateEmbeddableAction.class)); popup.add(buildMenu(CreateDbEntityAction.class)); popup.add(buildMenu(CreateProcedureAction.class)); popup.add(buildMenu(CreateQueryAction.class)); popup.addSeparator(); popup.add(buildMenu(ObjEntitySyncAction.class)); popup.add(buildMenu(DbEntitySyncAction.class)); popup.add(buildMenu(LinkDataMapsAction.class)); popup.add(buildMenu(RemoveAction.class)); popup.add(buildMenu(CutAction.class)); popup.add(buildMenu(CopyAction.class)); popup.add(buildMenu(PasteAction.class)); return popup; } /** * Creates and returns an menu item associated with the key. * * @param actionType action type */ private JMenuItem buildMenu(Class actionType) { CayenneAction action = (CayenneAction) mediator .getApplication() .getActionManager() .getAction(actionType); return action.buildMenu(); } /** * Class to handle: * - right-click and show popup for selected tree row * - left click row selection based on full row length (instead of default selection based on label size) */ class MouseClickHandler extends MouseAdapter { void selectRowForEvent(MouseEvent e) { int closestRow = getClosestRowForLocation(e.getX(), e.getY()); Rectangle closestRowBounds = getRowBounds(closestRow); if(e.getY() >= closestRowBounds.getY() && e.getY() < closestRowBounds.getY() + closestRowBounds.getHeight() && !isRowSelected(closestRow)) { setSelectionRow(closestRow); } } @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isLeftMouseButton(e)) { selectRowForEvent(e); } mouseReleased(e); } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { // Selecting specified row selectRowForEvent(e); if (popup == null) { popup = createJPopupMenu(); } popupMenuFilter(); popup.show(ProjectTreeView.this, e.getX(), e.getY()); } } } public void embeddableAdded(EmbeddableEvent e, DataMap map) { Embeddable embeddable = e.getEmbeddable(); DefaultMutableTreeNode mapNode = getProjectModel().getNodeForObjectPath( new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator .getProject() .getRootNode(), map }); if (mapNode == null) { return; } DefaultMutableTreeNode currentNode = new DefaultMutableTreeNode(embeddable, false); positionNode(mapNode, currentNode, Comparators.getDataMapChildrenComparator()); showNode(currentNode); } public void embeddableChanged(EmbeddableEvent e, DataMap map) { if (e.isNameChange()) { Object[] path = new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator.getProject().getRootNode(), map, e.getEmbeddable() }; updateNode(path); positionNode(path, Comparators.getDataMapChildrenComparator()); showNode(path); } } public void embeddableRemoved(EmbeddableEvent e, DataMap map) { if (e.getSource() == this) { return; } // remove from DataMap tree removeNode(new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator .getProject() .getRootNode(), map, e.getEmbeddable() }); } public void currentEmbeddableChanged(EmbeddableDisplayEvent e) { e.setEmbeddableChanged(true); if ((e.getSource() == this || !e.isEmbeddableChanged()) && !e.isRefired()) { return; } showNode(new Object[] { e.getDomain(), e.getDataMap(), e.getEmbeddable() }); } // Filter all disabled actions in popupMenu, but skip Cut-Copy-Paste block. It should always exist. public void popupMenuFilter() { Action cutAction = mediator.getApplication().getActionManager().getAction(CutAction.class); for (MenuElement element : popup.getSubElements()) { JMenuItem item = (JMenuItem) element; if (!item.getAction().equals(cutAction)) { item.setVisible(item.isEnabled()); } else { break; } } } public TreeDragSource getTds() { return tds; } @Override public void paintComponent(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); if (getSelectionCount() > 0) { g.setColor(SELECTION_COLOR); int[] rows = getSelectionRows(); if(rows != null) { for (int i : rows) { Rectangle r = getRowBounds(i); g.fillRect(0, r.y, getWidth(), r.height); } } } super.paintComponent(g); } }