/***************************************************************** * 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 * * http://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.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.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.ExpandVetoException; 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() { 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(); ConfigurationNode projectPath = (ConfigurationNode) ((DefaultMutableTreeNode) path[path.length - 1]) .getUserObject(); return projectPath; } }; treeWillExpandListener = new TreeWillExpandListener() { @Override public void treeWillExpand(TreeExpansionEvent e) throws ExpandVetoException { TreePath path = e.getPath(); if (!isPathSelected(path) && !isSelectionEmpty()) { setSelectionPath(path); } } @Override public void treeWillCollapse(TreeExpansionEvent e) throws ExpandVetoException { TreePath path = e.getPath(); if (!isPathSelected(path) && !isSelectionEmpty()) { setSelectionPath(path); } } }; addTreeSelectionListener(treeSelectionListener); addTreeWillExpandListener(treeWillExpandListener); addMouseListener(new MouseClickHandler()); 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) { String objectClass = (value == null) ? "(unknown)" : value.getClass().getName(); logObj.warn("Exception reading property 'name', class " + objectClass, 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[] { (DataChannelDescriptor) mediator.getProject().getRootNode(), e.getProcedure().getDataMap(), e.getProcedure() }; updateNode(path); positionNode(path, Comparators.getDataMapChildrenComparator()); showNode(path); } } public void procedureRemoved(ProcedureEvent e) { removeNode(new Object[] { (DataChannelDescriptor) 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) { DefaultMutableTreeNode domainNode = getProjectModel().getNodeForObjectPath( new Object[] { e.getDomain() != null ? e.getDomain() : (DataChannelDescriptor) mediator.getProject().getRootNode() }); DefaultMutableTreeNode newMapNode = ProjectTreeFactory.wrapProjectNode(e .getDataMap()); mediator.getEntityResolver().addDataMap(e.getDataMap()); positionNode(domainNode, newMapNode, Comparators .getDataDomainChildrenComparator()); showNode(newMapNode); } 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. * <ul> * <li>If entity is from the current node, refreshes the node making sure changes in * the entity name are reflected.</li> * <li>If entity is in a different node, makes that node visible and selected.</li> * </ul> */ 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); } 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<Object> 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.addSeparator(); popup.add(buildMenu(LinkDataMapsAction.class)); popup.addSeparator(); popup.add(buildMenu(RemoveAction.class)); popup.addSeparator(); 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<? extends Action> 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(); } 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() }); } 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); } }