/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.wcs.netbeans.liquiface.ui.widget; /* * #%L * Liquiface - GUI for Liquibase * %% * Copyright (C) 2013 Webstar Csoport Kft. * %% * This program 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 3 of the * License, or (at your option) any later version. * * This program 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 this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import com.wcs.netbeans.liquiface.facade.SceneFacade; import com.wcs.netbeans.liquiface.model.Column; import com.wcs.netbeans.liquiface.model.ForeignKeyConstraint; import com.wcs.netbeans.liquiface.model.Table; import java.awt.Image; import java.awt.Point; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.netbeans.api.visual.action.ActionFactory; import org.netbeans.api.visual.action.WidgetAction; import org.netbeans.api.visual.anchor.Anchor; import org.netbeans.api.visual.anchor.AnchorFactory; import org.netbeans.api.visual.graph.GraphPinScene; import org.netbeans.api.visual.graph.layout.GridGraphLayout; import org.netbeans.api.visual.layout.LayoutFactory; import org.netbeans.api.visual.layout.SceneLayout; import org.netbeans.api.visual.router.Router; import org.netbeans.api.visual.router.RouterFactory; import org.netbeans.api.visual.vmd.VMDColorScheme; import org.netbeans.api.visual.vmd.VMDConnectionWidget; import org.netbeans.api.visual.vmd.VMDFactory; import org.netbeans.api.visual.vmd.VMDNodeWidget; import org.netbeans.api.visual.widget.ConnectionWidget; import org.netbeans.api.visual.widget.EventProcessingType; import org.netbeans.api.visual.widget.LayerWidget; import org.netbeans.api.visual.widget.Widget; import org.openide.util.ImageUtilities; /** * * @author athalay */ public class LiquifaceGraphScene extends GraphPinScene<Integer, String, Integer>{ public static final String PIN_ID_DEFAULT_SUFFIX = "#default"; // NOI18N private final Image PRIMARY_KEY_ICON = ImageUtilities.loadImage("icons/column/primary.gif"); private final Image FOREIGN_KEY_ICON = ImageUtilities.loadImage("icons/column/foreign.png"); private final Image NOT_NULL_ICON = ImageUtilities.loadImage("icons/column/star.png"); private LayerWidget backgroundLayer = new LayerWidget (this); private LayerWidget mainLayer = new LayerWidget (this); private LayerWidget connectionLayer = new LayerWidget (this); private LayerWidget upperLayer = new LayerWidget (this); private Router router; private Router directRouter; private Router orthogonalRouter; private WidgetAction moveControlPointAction = ActionFactory.createOrthogonalMoveControlPointAction (); private WidgetAction moveAction = ActionFactory.createMoveAction (); private SceneLayout sceneLayout; private VMDColorScheme scheme; private Map<Integer, TableWidget> widgets = new HashMap<Integer, TableWidget>(); public LiquifaceGraphScene() { this (VMDFactory.getOriginalScheme()); } /** * Creates a VMD graph scene with a specific color scheme. * @param scheme the color scheme */ public LiquifaceGraphScene (VMDColorScheme scheme) { this.scheme = scheme; setKeyEventProcessingType (EventProcessingType.FOCUSED_WIDGET_AND_ITS_PARENTS); addChild (backgroundLayer); addChild (mainLayer); addChild (connectionLayer); addChild (upperLayer); orthogonalRouter = RouterFactory.createOrthogonalSearchRouter (mainLayer, connectionLayer); directRouter = RouterFactory.createDirectRouter (); router = orthogonalRouter; getActions ().addAction (ActionFactory.createZoomAction ()); getActions ().addAction (ActionFactory.createPanAction ()); getActions ().addAction (ActionFactory.createWheelPanAction ()); getActions ().addAction (ActionFactory.createRectangularSelectAction (this, backgroundLayer)); sceneLayout = LayoutFactory.createSceneGraphLayout (this, new GridGraphLayout<Integer, String> ().setChecker (true)); } public boolean containsTable(Integer tableId){ return widgets.containsKey(tableId); } public void refreshTables(List<Table> tables){ removeNonExistentTableNodes(tables); for (Table table : tables) { if(widgets.containsKey(table.getId())){ refreshTable(table); } else { addTable(table, new Point(50, 50)); } } } private void removeNonExistentTableNodes(List<Table> tables){ List<Integer> removedWidgets = new ArrayList(); for (Integer tableId : widgets.keySet()) { if(isTableMissingFromList(tables, tableId)){ TableWidget tableToRemove = widgets.get(tableId); mainLayer.removeChild(tableToRemove); removedWidgets.add(tableId); } } for (Integer tableId : removedWidgets) { widgets.remove(tableId); removeNode(tableId); } } public boolean isTableMissingFromList(List<Table> tables, Integer tableId){ for (Table table : tables) { if(table.getId() == tableId.intValue()){ return false; } } return true; } public void refreshTable(Table table){ TableWidget tableWidget = widgets.get(table.getId()); tableWidget.setNodeName(table.getName()); List<Integer> columnWidgetIds = tableWidget.getColumnWidgetsIds(); for (Column column : table.getColumns()) { if(columnWidgetIds.contains(column.getId())){ ColumnWidget columnWidget = (ColumnWidget)findWidget(column.getId()); columnWidget.setProperties(PIN_ID_DEFAULT_SUFFIX, null); columnWidget.refresh(column, getGlyps(column, table)); } else { addColumnWidget(column, table); } validate(); } for (Integer columnWidgetId : columnWidgetIds) { if(table.getColumnById(columnWidgetId) == null){ removePinWithEdges(columnWidgetId); } } validate(); } public void addTable(Table table, Point position){ TableWidget tableWidget = (TableWidget) addNode(table.getId()); tableWidget.setTableId(table.getId()); tableWidget.setPreferredLocation (position); tableWidget.setNodeProperties (null, table.getName(), null, null); for (Column column : table.getColumns()) { addColumnWidget(column, table); } WidgetAction popupAction = ActionFactory.createPopupMenuAction(SceneFacade.getInstance()); tableWidget.getActions().addAction(popupAction); validate(); } private void addColumnWidget(Column column, Table table){ ColumnWidget columnWidget = (ColumnWidget)addPin (table.getId(), column.getId()); WidgetAction popupAction = ActionFactory.createPopupMenuAction(SceneFacade.getInstance()); columnWidget.getActions().addAction(popupAction); columnWidget.refresh(column, getGlyps(column, table)); validate(); } private List<Image> getGlyps(Column column, Table table){ List<Image> glyps = new ArrayList<Image>(); if(column.isPrimaryKey()){ glyps.add(PRIMARY_KEY_ICON); } if(table.isForeignKey(column)){ glyps.add(FOREIGN_KEY_ICON); } if(!column.isNullable()){ glyps.add(NOT_NULL_ICON); } return glyps; } public void refreshColumn(Column column, Table table){ ColumnWidget columnWidget = (ColumnWidget)findWidget(column.getId()); columnWidget.refresh(column, getGlyps(column, table)); validate(); } public void refreshForeignKeys(List<Table> tables){ clearForeignKeys(); for (Table table : tables) { for (ForeignKeyConstraint foreignKeyConstraint : table.getForeignKeyConstraints()) { if(tables.contains(foreignKeyConstraint.getReferencedTable())){ createEdge(foreignKeyConstraint); } validate(); } } } public void clearForeignKeys(){ List<String> originalEdges = new ArrayList<String>(getEdges()); List<String> edges = new ArrayList<String>(originalEdges); Collections.copy(edges, originalEdges); for (String edge : edges) { removeEdge(edge); } } private void createEdge(ForeignKeyConstraint foreignKeyConstraint){ String edgeID = foreignKeyConstraint.getName(); Integer refPinId = foreignKeyConstraint.getReferencedColumns().get(0).getId(); Integer pinId = foreignKeyConstraint.getBaseColumns().get(0).getId(); addEdge(edgeID); setEdgeSource (edgeID, pinId); setEdgeTarget (edgeID, refPinId); } /** * Implements attaching a widget to a node. The widget is VMDNodeWidget and has object-hover, select, popup-menu and move actions. * @param node the node * @return the widget attached to the node */ @Override protected Widget attachNodeWidget (Integer node) { TableWidget widget = new TableWidget(this, scheme); mainLayer.addChild (widget); widget.getHeader ().getActions ().addAction (createObjectHoverAction ()); widget.getActions ().addAction (createSelectAction ()); widget.getActions ().addAction (moveAction); widgets.put(node, widget); return widget; } public void removeNodeWidget(Integer node){ mainLayer.removeChild(widgets.get(node)); } /** * Implements attaching a widget to a pin. The widget is VMDPinWidget and has object-hover and select action. * The the node id ends with "#default" then the pin is the default pin of a node and therefore it is non-visual. * @param node the node * @param pin the pin * @return the widget attached to the pin, null, if it is a default pin */ @Override protected Widget attachPinWidget (Integer node, Integer pin) { ColumnWidget widget = new ColumnWidget (this, scheme, pin); ((TableWidget) findWidget (node)).attachPinWidget (widget); widget.getActions ().addAction (createObjectHoverAction ()); widget.getActions ().addAction (createSelectAction ()); return widget; } /** * Implements attaching a widget to an edge. the widget is ConnectionWidget and has object-hover, select and move-control-point actions. * @param edge the edge * @return the widget attached to the edge */ @Override protected Widget attachEdgeWidget (String edge) { VMDConnectionWidget connectionWidget = new VMDConnectionWidget (this, scheme); connectionWidget.setRouter (router); connectionLayer.addChild (connectionWidget); connectionWidget.getActions ().addAction (createObjectHoverAction ()); connectionWidget.getActions ().addAction (createSelectAction ()); connectionWidget.getActions ().addAction (moveControlPointAction); return connectionWidget; } /** * Attaches an anchor of a source pin an edge. * The anchor is a ProxyAnchor that switches between the anchor attached to the pin widget directly and * the anchor attached to the pin node widget based on the minimize-state of the node. * @param edge the edge * @param oldSourcePin the old source pin * @param sourcePin the new source pin */ @Override protected void attachEdgeSourceAnchor (String edge, Integer oldSourcePin, Integer sourcePin) { ((ConnectionWidget) findWidget (edge)).setSourceAnchor (getPinAnchor (sourcePin)); } /** * Attaches an anchor of a target pin an edge. * The anchor is a ProxyAnchor that switches between the anchor attached to the pin widget directly and * the anchor attached to the pin node widget based on the minimize-state of the node. * @param edge the edge * @param oldTargetPin the old target pin * @param targetPin the new target pin */ @Override protected void attachEdgeTargetAnchor (String edge, Integer oldTargetPin, Integer targetPin) { ((ConnectionWidget) findWidget (edge)).setTargetAnchor (getPinAnchor (targetPin)); } private Anchor getPinAnchor (Integer pin) { if (pin == null) return null; VMDNodeWidget nodeWidget = (VMDNodeWidget) findWidget (getPinNode (pin)); Widget pinMainWidget = findWidget (pin); Anchor anchor; if (pinMainWidget != null) { anchor = AnchorFactory.createDirectionalAnchor (pinMainWidget, AnchorFactory.DirectionalAnchorKind.HORIZONTAL, 8); anchor = nodeWidget.createAnchorPin (anchor); } else anchor = nodeWidget.getNodeAnchor (); return anchor; } /** * Invokes layout of the scene. */ public void layoutScene () { sceneLayout.invokeLayout (); } public void setDirectRouter() { this.router = directRouter; } public void setOrthogonalRouter() { this.router = orthogonalRouter; } public boolean isDirectRouterUsed() { return router.equals(directRouter); } }