package iiuf.swing.graph; import java.io.File; import java.util.HashMap; import java.util.StringTokenizer; import java.awt.Point; import java.awt.Container; import java.awt.LayoutManager; import java.awt.LayoutManager2; import java.awt.BorderLayout; import java.awt.GridBagLayout; import java.awt.Dimension; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.AWTEvent; import java.awt.Cursor; import java.awt.Color; import java.awt.Rectangle; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyAdapter; import java.awt.dnd.DropTargetListener; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTarget; import java.awt.dnd.DnDConstants; import javax.swing.Action; import javax.swing.AbstractAction; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JButton; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JViewport; import javax.swing.JTextField; import javax.swing.JScrollPane; import javax.swing.JComboBox; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.SwingConstants; import javax.swing.InputMap; import javax.swing.ActionMap; import javax.swing.KeyStroke; import iiuf.awt.Awt; import iiuf.swing.Swing; import iiuf.swing.SetSelectionModel; import iiuf.swing.ContextMenuEnabled; import iiuf.swing.ContextMenuManager; import iiuf.swing.ContextMenu; import iiuf.swing.Resource; import iiuf.util.AttributeFactory; import iiuf.util.Attributable; import iiuf.util.Util; import iiuf.util.Unicode; import iiuf.util.graph.GraphNode; import iiuf.util.graph.GraphPort; import iiuf.util.graph.GraphModel; import iiuf.util.graph.DefaultGraphNode; import iiuf.util.graph.DefaultGraphEdge; import iiuf.util.graph.GraphModelListener; /** Graph editor implementation.<p> (c) 2000, 2001, IIUF, DIUF<p> @author $Author: ohitz $ @version $Name: $ $Revision: 1.1 $ */ public class GraphPanelEditor extends JComponent implements MouseListener, MouseMotionListener, ContextMenuEnabled, DropTargetListener, SwingConstants { private final static int MIN_RESIZE = 16; private GraphPanel gp; private int dx; private int dy; private int ox; private int oy; private Rectangle[] resizeSnap; private Component[] resizeSnapCmp; private Rectangle resizeSnapBounds; private Rectangle tmpRect = new Rectangle(); private int dragArea; private Cursor defaultCursor; private Cursor dragCursor = new Cursor(Cursor.MOVE_CURSOR); private Cursor NECursor = new Cursor(Cursor.NE_RESIZE_CURSOR); private Cursor ECursor = new Cursor(Cursor.E_RESIZE_CURSOR); private Cursor SECursor = new Cursor(Cursor.SE_RESIZE_CURSOR); private Cursor NCursor = new Cursor(Cursor.N_RESIZE_CURSOR); private Cursor SCursor = new Cursor(Cursor.S_RESIZE_CURSOR); private Cursor NWCursor = new Cursor(Cursor.NW_RESIZE_CURSOR); private Cursor WCursor = new Cursor(Cursor.W_RESIZE_CURSOR); private Cursor SWCursor = new Cursor(Cursor.SW_RESIZE_CURSOR); private Cursor connectCursor = new Cursor(Cursor.CROSSHAIR_CURSOR); private Cursor stopCursor = Awt.STOP_CURSOR; private MouseEvent lastMouseEvent; GraphNode dragable; iiuf.util.graph.GraphEdge connectingEdge; ConnectingNode connectingNode = new ConnectingNode(); GraphPort fromPort; JMenu alignMenu = new JMenu("Align"); JMenu distributeMenu = new JMenu("Distribute"); JMenu rotateMenu = new JMenu("Rotate"); JMenu arrangeMenu = new JMenu("Arrange"); Component invisible = Awt.newComponent(); private static final int MOVING = 0; private static final int CONNECTING = 1; private static final int DRAGGING = 2; private static final int SELECTING = 3; private int state = MOVING; private Action[] editActions = { new AbstractAction("Delete") { boolean init = init(); public void actionPerformed(ActionEvent e) {delete();} boolean init() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("DELETE")); return true; } }, new AbstractAction("Select All") { boolean init = init(); public void actionPerformed(ActionEvent e) {selectAll();} boolean init() { putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl A")); return true; } }, }; private Action[] propertiesActions = { new AbstractAction("Properties") { public void actionPerformed(ActionEvent e) {properties(gp.viewToModel((Component)ctxmgr.getLastObject()));} }, }; private JMenuItem[] ctxMenuEditItems = {Swing.newMenuItem(editActions[0], (KeyStroke)editActions[0].getValue(Action.ACCELERATOR_KEY))}; private JMenuItem[] ctxMenuPropertiesItems = {new JMenuItem(propertiesActions[0])}; private JMenuItem[] ctxMenuAlignItems = {alignMenu, distributeMenu}; private JMenuItem[] ctxMenuRotateItems = {rotateMenu}; private JMenuItem[] ctxMenuArrangeItems = {arrangeMenu}; GraphPanelEditor(GraphPanel gp_) { addMouseListener(this); addMouseMotionListener(this); gp = gp_; for(int i = 0; i < alignActions.length; i++) alignMenu.add(new JMenuItem(alignActions[i])); for(int i = 0; i < distributeActions.length; i++) distributeMenu.add(new JMenuItem(distributeActions[i])); for(int i = 0; i < rotateActions.length; i++) rotateMenu.add(new JMenuItem(rotateActions[i])); for(int i = 0; i < arrangeActions.length; i++) arrangeMenu.add(new JMenuItem(arrangeActions[i])); addActions(editActions); addActions(propertiesActions); addActions(alignActions); addActions(distributeActions); addActions(rotateActions); addActions(arrangeActions); addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if(e.getKeyCode() == e.VK_SHIFT && lastMouseEvent != null) { gp.retargetMouseEvent((Component)lastMouseEvent.getSource(), lastMouseEvent.getID(), e.getModifiers(), lastMouseEvent); } } public void keyReleased(KeyEvent e) { if(e.getKeyCode() == e.VK_SHIFT && lastMouseEvent != null) { gp.retargetMouseEvent((Component)lastMouseEvent.getSource(), lastMouseEvent.getID(), e.getModifiers() & ~e.SHIFT_MASK, lastMouseEvent); } } }); InputMap imap = getInputMap(); imap.put(KeyStroke.getKeyStroke("BACK_SPACE"), editActions[0].getValue(Action.NAME)); for(int i = 0; i < editActions.length; i++) imap.put((KeyStroke)editActions[i].getValue(Action.ACCELERATOR_KEY), editActions[i].getValue(Action.NAME)); } public void start() { defaultCursor = getCursor(); } public void stop() { gp.setDot(dx, dy); gp.setMark(dx, dy); gp.setShowPorts(null, null); setCursor(defaultCursor); } public Component getComponent() {return this;} public Object locationToObject(Component component, Point location) { Object result = gp.locationToObject(gp, location); return result; } private ContextMenuManager ctxmgr; public void setContextMenuManager(ContextMenuManager manager) { gp.setContextMenuManager(manager); if(manager != ctxmgr) { manager.addContextMenu(new ContextMenu() { public boolean check(Object o) { return gp.getSelectionModel().size(GraphNode.class) > 0; } public JMenuItem[] getItems() { return ctxMenuArrangeItems; } }, true); manager.addContextMenu(new ContextMenu() { public boolean check(Object o) { return gp.getLayouter().allowsNodeLocationChange() && gp.getSelectionModel().size(GraphNode.class) > 1; } public JMenuItem[] getItems() { return ctxMenuAlignItems; } }, true); manager.addContextMenu(new ContextMenu() { public boolean check(Object o) { return gp.getLayouter().allowsNodeLocationChange() && gp.getSelectionModel().size(GraphNode.class) <= 1 && o instanceof GraphNodeComponent && GraphNodeComponent.ANGLE_90 % ((GraphNodeComponent)o).getMinimalRotation() == 0; } public JMenuItem[] getItems() { return ctxMenuRotateItems; } }, true); manager.addContextMenu(new ContextMenu() { public boolean check(Object o) { return o instanceof Component && gp.viewToModel((Component)o) != null && gp.viewToModel((Component)o).get(gp.NODE_PROPERTIES) != null; } public JMenuItem[] getItems() { return ctxMenuPropertiesItems; } }, true); manager.addContextMenu(new ContextMenu() { public boolean check(Object o) { return !gp.getSelectionModel().isEmpty(); } public JMenuItem[] getItems() { return ctxMenuEditItems; } }, true); ctxmgr = manager; } } private Cursor lastCursor; public void setCursor(Cursor crsr) { if(crsr != lastCursor) super.setCursor(crsr); lastCursor = crsr; } private void setResizeCursor() { switch(dragArea) { case NORTH_EAST: setCursor(NECursor); break; case EAST: setCursor(ECursor); break; case SOUTH_EAST: setCursor(SECursor); break; case NORTH: setCursor(NCursor); break; case SOUTH: setCursor(SCursor); break; case NORTH_WEST: setCursor(NWCursor); break; case WEST: setCursor(WCursor); break; case SOUTH_WEST: setCursor(SWCursor); break; } } public void mouseClicked(MouseEvent e) { lastMouseEvent = null; dx = e.getX(); dy = e.getY(); switch(state) { case MOVING: { int modif = e.getModifiers(); if((modif & e.BUTTON1_MASK) != 0) { if(!e.isShiftDown()) gp.getSelectionModel().clearSelection(); Object o = gp.viewToModel(e.getPoint()); if(o instanceof GraphNode || o instanceof iiuf.util.graph.GraphEdge) if(gp.getSelectionModel().isSelected(o)) gp.getSelectionModel().remove(o); else gp.getSelectionModel().add(o); } gp.setShowPorts(null, null); break; } case CONNECTING: { endConnect(); state = MOVING; break; } case DRAGGING: setCursor(defaultCursor); state = MOVING; break; case SELECTING: select(dx, dy); if(!e.isShiftDown()) gp.getSelectionModel().clearSelection(); state = MOVING; break; } } public void mouseEntered(MouseEvent e) { lastMouseEvent = null; requestFocus(); } public void mouseExited(MouseEvent e) { lastMouseEvent = null; gp.setShowPorts(null, null); } public void mousePressed(MouseEvent e) { lastMouseEvent = null; dx = e.getX(); dy = e.getY(); switch(state) { case MOVING: break; case CONNECTING: endConnect(); state = MOVING; break; case DRAGGING: setCursor(defaultCursor); state = MOVING; break; case SELECTING: select(dx, dy); state = MOVING; break; } } public void mouseReleased(MouseEvent e) { lastMouseEvent = null; dx = e.getX(); dy = e.getY(); switch(state) { case MOVING: break; case CONNECTING: endConnect(); state = MOVING; break; case DRAGGING: setCursor(defaultCursor); state = MOVING; break; case SELECTING: select(dx, dy); state = MOVING; break; } } public void mouseMoved(MouseEvent e) { lastMouseEvent = e; requestFocus(); dx = e.getX(); dy = e.getY(); Object o = gp.viewToModel(e.getPoint()); switch(state) { case MOVING: if(gp.getSelectionModel().isEmpty() && o instanceof GraphNode && !(o instanceof ConnectingNode)) gp.setShowPorts((GraphNode)o, gp.findPortAt((GraphNode)o, dx, dy)); else gp.setShowPorts(null, null); setCursor(defaultCursor); break; case CONNECTING: endConnect(); state = MOVING; break; case DRAGGING: setCursor(defaultCursor); state = MOVING; break; case SELECTING: select(dx, dy); state = MOVING; break; } gp.handleMouseMoved(e); } public void mouseDragged(MouseEvent e) { lastMouseEvent = e; int x = e.getX(); int y = e.getY(); switch(state) { case MOVING: int modif = e.getModifiers(); if((modif & e.BUTTON1_MASK) != 0) { dragArea = gp.pointToSelectionArea(dx, dy); setResizeCursor(); switch(dragArea) { case NORTH_EAST: case EAST: case SOUTH_EAST: case NORTH: case SOUTH: case NORTH_WEST: case WEST: case SOUTH_WEST: resizeSnap(); state = DRAGGING; break; default: Object o = gp.viewToModel(dx, dy); if(gp.getSelectionModel().isEmpty() && o instanceof GraphNode) { GraphNode node = (GraphNode)o; fromPort = gp.findPortAt(node, dx, dy); if(fromPort == null) return; if(fromPort.isFull()) { fromPort = null; setCursor(stopCursor); return; } gp.getModel().add(connectingNode); connectingEdge = fromPort.createEdge(connectingNode.getDefaultPort()); gp.getModel().add(connectingEdge); ((Component)connectingNode.get(gp.COMPONENT)).setLocation(e.getPoint()); setCursor(connectCursor); state = CONNECTING; return; } else { dragable = gp.findNodeAt(dx, dy); if(dragable == null || !gp.getSelectionModel().isSelected(dragable)) { gp.setDot(dx, dy); gp.setMark(x, y); if(!e.isShiftDown()) gp.getSelectionModel().clearSelection(); state = SELECTING; return; } Component dragableCmp = (Component)dragable.get(gp.COMPONENT); if(gp.getLayouter().allowsNodeLocationChange()) setCursor(dragCursor); ox = dragableCmp.getX() - dx; oy = dragableCmp.getY() - dy; } state = DRAGGING; } } break; case CONNECTING: { ((Component)connectingNode.get(gp.COMPONENT)).setLocation(0, 0); Object o = gp.viewToModel(e.getPoint()); if(o instanceof GraphNode) { GraphNode node = (GraphNode)o; GraphPort port = gp.findPortAt(node, x, y); setCursor(port.compatible(fromPort) && !port.isFull() ? connectCursor : stopCursor); ((Component)connectingNode.get(gp.COMPONENT)). setLocation(((GraphNodePort)port.get(gp.GRAPH_NODE_PORT)). getLocation(((Component)node.get(gp.COMPONENT)))); gp.setShowPorts(node, port); } else { ((Component)connectingNode.get(gp.COMPONENT)).setLocation(e.getPoint()); gp.setShowPorts(null, null); setCursor(connectCursor); } gp.repaint(); break; } case DRAGGING: { if(gp.getLayouter().allowsNodeLocationChange()) { if(dragArea != CENTER && dragArea != -1) { Rectangle dst = (Rectangle)gp.getSelectionBounds().clone(); int mdx = x - dx; int mdy = y - dy; int xlim = resizeSnapBounds.x; int ylim = resizeSnapBounds.y; switch(dragArea) { case NORTH_EAST: dst.width = resizeSnapBounds.width + mdx; dst.height = resizeSnapBounds.height - mdy; dst.y = resizeSnapBounds.y + mdy; ylim = resizeSnapBounds.y + resizeSnapBounds.height - MIN_RESIZE; break; case EAST: dst.width = resizeSnapBounds.width + mdx; break; case SOUTH_EAST: dst.width = resizeSnapBounds.width + mdx; dst.height = resizeSnapBounds.height + mdy; break; case NORTH: dst.height = resizeSnapBounds.height - mdy; dst.y = resizeSnapBounds.y + mdy; ylim = resizeSnapBounds.y + resizeSnapBounds.height - MIN_RESIZE; break; case SOUTH: dst.height = resizeSnapBounds.height + mdy; break; case NORTH_WEST: dst.width = resizeSnapBounds.width - mdx; dst.x = resizeSnapBounds.x + mdx; dst.height = resizeSnapBounds.height - mdy; dst.y = resizeSnapBounds.y + mdy; xlim = resizeSnapBounds.x + resizeSnapBounds.width - MIN_RESIZE; ylim = resizeSnapBounds.y + resizeSnapBounds.height - MIN_RESIZE; break; case WEST: dst.width = resizeSnapBounds.width - mdx; dst.x = resizeSnapBounds.x + mdx; xlim = resizeSnapBounds.x + resizeSnapBounds.width - MIN_RESIZE; break; case SOUTH_WEST: dst.width = resizeSnapBounds.width - mdx; dst.x = resizeSnapBounds.x + mdx; dst.height = resizeSnapBounds.height + mdy; xlim = resizeSnapBounds.x + resizeSnapBounds.width - MIN_RESIZE; break; } if(dst.width < MIN_RESIZE) { dst.width = MIN_RESIZE; dst.x = xlim; } if(dst.height < MIN_RESIZE) { dst.height = MIN_RESIZE; dst.y = ylim; } if(e.isShiftDown()) switch(dragArea) { case SOUTH: case NORTH: dst.width = (resizeSnapBounds.width * dst.height) / resizeSnapBounds.height; dst.width &= 0xFFFFFFFE; dst.x = resizeSnapBounds.x + (resizeSnapBounds.width - dst.width) / 2; break; case EAST: case WEST: case SOUTH_EAST: case SOUTH_WEST: case NORTH_WEST: case NORTH_EAST: dst.height = (resizeSnapBounds.height * dst.width) / resizeSnapBounds.width; if(dragArea == EAST || dragArea == WEST) { dst.height &= 0xFFFFFFFE; dst.y = resizeSnapBounds.y + (resizeSnapBounds.height - dst.height) / 2; } if(dragArea == NORTH_WEST || dragArea == NORTH_EAST) dst.y = resizeSnapBounds.y + resizeSnapBounds.height - dst.height; break; } resizeSelection(dst); } else if(dragable != null){ Component dragableCmp = (Component)dragable.get(gp.COMPONENT); if(!gp.getSelectionModel().isEmpty() && gp.getSelectionModel().isSelected(dragable)) { Object[] sel = gp.getSelectionModel().getSelection(); int dragX = dragableCmp.getX(); int dragY = dragableCmp.getY(); Rectangle r = null; for(int i = 0; i < sel.length; i++) { if(sel[i] instanceof GraphNode) { tmpRect = ((Component)((GraphNode)sel[i]).get(gp.COMPONENT)).getBounds(tmpRect); if(r == null) r = (Rectangle)tmpRect.clone(); r.add(tmpRect); } } Point loc = Awt.fitSmallerIntoBigger(x + ox + r.x - dragX, y + oy + r.y - dragY, r.width, r.height, getBounds(tmpRect)); for(int i = 0; i < sel.length; i++) { if(!(sel[i] instanceof GraphNode)) continue; Component c = (Component)((GraphNode)sel[i]).get(gp.COMPONENT); c.setLocation(c.getX() - r.x + loc.x, c.getY() - r.y + loc.y); } } } gp.repaint(); } break; } case SELECTING: gp.setMark(x, y); break; } } public void resizeSelection(int x, int y, int w, int h) { resizeSnap(); if(w < MIN_RESIZE) w = MIN_RESIZE; if(h < MIN_RESIZE) h = MIN_RESIZE; resizeSelection(new Rectangle(x, y, w, h)); gp.repaint(); } private void resizeSnap() { resizeSnapBounds = gp.getSelectionBounds(); Object[] sel = gp.getSelectionModel().getSelection(); int count = 0; for(int i = 0; i < sel.length; i++) if(sel[i] instanceof GraphNode) count++; resizeSnap = new Rectangle[count]; resizeSnapCmp = new Component[count]; count = 0; for(int i = 0; i < sel.length; i++) if(sel[i] instanceof GraphNode) { resizeSnapCmp[count] = (Component)((GraphNode)sel[i]).get(gp.COMPONENT); resizeSnap[count] = resizeSnapCmp[count].getBounds(); count++; } } private void resizeSelection(Rectangle dst) { for(int i = 0; i < resizeSnapCmp.length; i++) { tmpRect = scaleRect(resizeSnapBounds, dst, resizeSnap[i], tmpRect); resizeSnapCmp[i].setBounds(tmpRect); int cx = tmpRect.x; int cy = tmpRect.y; int cw = resizeSnapCmp[i].getWidth(); int ch = resizeSnapCmp[i].getHeight(); int dx2 = dst.x + dst.width; int dy2 = dst.y + dst.height; boolean reloc = false; if(cw != tmpRect.width) { if(cx != dst.x && tmpRect.x + tmpRect.width == dx2) { cx = dx2 - cw; if(cx < resizeSnapBounds.x) cx = resizeSnapBounds.x; reloc = true; } if(cw > dst.width && (dragArea == WEST || dragArea == NORTH_WEST || dragArea == NORTH || dragArea == SOUTH_WEST)) { cx = resizeSnapBounds.x + resizeSnapBounds.width - cw; reloc = true; } if(resizeSnapCmp.length == 1) { cx = resizeSnapBounds.x; reloc = true; } } if(ch != tmpRect.height) { if(cy != dst.y && tmpRect.y + tmpRect.height == dy2) { cy = dy2 - ch; if(cy < resizeSnapBounds.y) cy = resizeSnapBounds.y; reloc = true; } if(ch > dst.height && (dragArea == WEST || dragArea == NORTH_WEST || dragArea == NORTH || dragArea == NORTH_EAST)) { cy = resizeSnapBounds.y + resizeSnapBounds.height - ch; reloc = true; } if(resizeSnapCmp.length == 1) { cy = resizeSnapBounds.y; reloc = true; } } if(reloc) resizeSnapCmp[i].setLocation(cx, cy); } } private Rectangle scaleRect(Rectangle src, Rectangle dst, Rectangle r, Rectangle result) { result.x = dst.x + (((r.x - src.x) * dst.width) / src.width); result.y = dst.y + (((r.y - src.y) * dst.height) / src.height); result.width = dst.x + (((r.x + r.width - src.x) * dst.width) / src.width); result.height = dst.y + (((r.y + r.height- src.y) * dst.height) / src.height); int tmp; if(result.width < result.x) { tmp = result.x; result.x = result.width; result.width = tmp; } if(result.height < result.y) { tmp = result.x; result.x = result.height; result.height = tmp; } result.width -= result.x; result.height -= result.y; return result; } void select(int x, int y) { Rectangle selection = gp.getDotMarkRectangle(); GraphNode[] nodes = gp.getModel().nodesArray(); for(int i = 0; i < nodes.length; i++) { Component c = (Component)nodes[i].get(gp.COMPONENT); if(selection.intersects(c.getBounds(tmpRect))) gp.getSelectionModel().add(nodes[i]); } iiuf.util.graph.GraphEdge[] edges = gp.getModel().edgesArray(); for(int i = 0; i < edges.length; i++) { GraphEdge e = (GraphEdge)edges[i].get(gp.GRAPH_EDGE); if(e.intersectsWith(selection)) gp.getSelectionModel().add(edges[i]); } gp.setDot(x, y); gp.setMark(x, y); } void endConnect() { ((Component)connectingNode.get(gp.COMPONENT)).setLocation(0, 0); Object o = gp.viewToModel(dx, dy); if(o instanceof GraphNode) { GraphNode node = (GraphNode)o; GraphPort port = gp.findPortAt(node, dx, dy); if(port != null && !port.isFull() && port.compatible(fromPort)) { if(connectingEdge.isTo(fromPort)) connectingEdge.setFrom(port); else connectingEdge.setTo(port); GraphNode fromNode = fromPort.getNode(); fromPort = null; int[] cmp_ids = gp.getModel().getIds(gp.COMPONENT_TAG); int[] gnp_ids = gp.getModel().getIds(gp.GRAPH_NODE_PORT_TAG); int[] edge_ids = gp.getModel().getIds(gp.EDGE_TAG); for(int i = 0; i < cmp_ids.length; i++) if(node.get(cmp_ids[i]) instanceof Component && port.get(gnp_ids[i]) instanceof GraphNodePort && connectingEdge.get(edge_ids[i]) instanceof GraphEdge) ((GraphEdge)connectingEdge.get(edge_ids[i])).setAdjacent((Component)fromNode.get(cmp_ids[i]), (Component)node.get(cmp_ids[i]), (GraphNodePort)port.get(gnp_ids[i])); } } gp.getModel().remove(connectingNode); setCursor(defaultCursor); } private void properties(GraphNode node) { Component props = (Component)node.get(gp.NODE_PROPERTIES); if(props != null) { Component c = (Component)node.get(gp.COMPONENT); Point loc = Awt.place(props.getBounds(), c.getX() + c.getWidth() / 2, c.getY() + c.getHeight(), getBounds()); loc.x += getLocationOnScreen().x; loc.y += getLocationOnScreen().y; props.setLocation(loc); props.setVisible(true); gp.repaint(); } } private void delete() { gp.getModel().remove((iiuf.util.graph.GraphEdge[])gp.getSelectionModel().getSelection(iiuf.util.graph.GraphEdge.class)); gp.getModel().remove((GraphNode[])gp.getSelectionModel().getSelection(GraphNode.class)); gp.getSelectionModel().clearSelection(); gp.setShowPorts(null, null); } public void selectAll() { gp.getSelectionModel().addAll(gp.getModel().nodes()); gp.getSelectionModel().addAll(gp.getModel().edges()); } public void dragEnter(DropTargetDragEvent e) {gp.dragEnter(e);} public void dragExit(DropTargetEvent e) {gp.dragExit(e);} public void dragOver(DropTargetDragEvent e) {gp.dragOver(e);} public void dropActionChanged(DropTargetDragEvent e) {gp.dropActionChanged(e);} public void drop(DropTargetDropEvent e) {gp.drop(e);} private static final int LEFT = 0; private static final int HORIZONTAL = 1; private static final int RIGHT = 2; private static final int TOP = 3; private static final int VERTICAL = 4; private static final int BOTTOM = 5; private void align(int direction) { Object[] sel = gp.getSelectionModel().getSelection(GraphNode.class); Component[] cs = new Component[sel.length]; for(int i = 0; i < cs.length; i++) cs[i] = (Component)((GraphNode)sel[i]).get(gp.COMPONENT); int min; int max; int c; switch(direction) { case LEFT: min = Integer.MAX_VALUE; for(int i = 0; i < cs.length; i++) if(cs[i].getX() < min) min = cs[i].getX(); for(int i = 0; i < cs.length; i++) cs[i].setLocation(min, cs[i].getY()); break; case HORIZONTAL: c = cs[0].getX() + cs[0].getWidth() / 2; for(int i = 0; i < cs.length; i++) cs[i].setLocation(c - cs[i].getWidth() / 2, cs[i].getY()); break; case RIGHT: max = Integer.MIN_VALUE; for(int i = 0; i < cs.length; i++) if(cs[i].getX() + cs[i].getWidth() > max) max = cs[i].getX() + cs[i].getWidth(); for(int i = 0; i < cs.length; i++) cs[i].setLocation(max - cs[i].getWidth(), cs[i].getY()); break; case TOP: min = Integer.MAX_VALUE; for(int i = 0; i < cs.length; i++) if(cs[i].getY() < min) min = cs[i].getY(); for(int i = 0; i < cs.length; i++) cs[i].setLocation(cs[i].getX(), min); break; case VERTICAL: c = cs[0].getY() + cs[0].getHeight() / 2; for(int i = 0; i < cs.length; i++) cs[i].setLocation(cs[i].getX(), c - cs[i].getHeight() / 2); break; case BOTTOM: max = Integer.MIN_VALUE; for(int i = 0; i < cs.length; i++) if(cs[i].getY() + cs[i].getHeight() > max) max = cs[i].getY() + cs[i].getHeight(); for(int i = 0; i < cs.length; i++) cs[i].setLocation(cs[i].getX(), max - cs[i].getHeight()); break; } gp.repaint(); } static class CmpCard {int card(Component c) {return 0;}} static class CmpCardL extends CmpCard {int card(Component c) {return c.getX();}} static private final CmpCard CMP_CARD_L = new CmpCardL(); static class CmpCardH extends CmpCard {int card(Component c) {return c.getX() + c.getWidth() / 2;}} static private final CmpCard CMP_CARD_H = new CmpCardH(); static class CmpCardR extends CmpCard {int card(Component c) {return c.getX() + c.getWidth();}} static private final CmpCard CMP_CARD_R = new CmpCardR(); static class CmpCardT extends CmpCard {int card(Component c) {return c.getY();}} static private final CmpCard CMP_CARD_T = new CmpCardT(); static class CmpCardV extends CmpCard {int card(Component c) {return c.getY() + c.getHeight() / 2;}} static private final CmpCard CMP_CARD_V = new CmpCardV(); static class CmpCardB extends CmpCard {int card(Component c) {return c.getY() + c.getHeight();}} static private final CmpCard CMP_CARD_B = new CmpCardB(); private int partition(Component[] a, int p, int r, CmpCard cc) { int x = cc.card(a[p]); int i = p - 1; int j = r + 1; for(;;) { do {j--;} while(cc.card(a[j]) > x); do {i++;} while(cc.card(a[i]) < x); if(i < j) { Component tmp = a[i]; a[i] = a[j]; a[j] = tmp; } else return j; } } private void sort(Component[] a, int p, int r, CmpCard cc) { if(p < r) { int q = partition(a, p, r, cc); sort(a, p, q, cc); sort(a, q + 1, r, cc); } } private void distribute(int direction, boolean space) { Object[] sel = gp.getSelectionModel().getSelection(GraphNode.class); Component[] cs = new Component[sel.length]; for(int i = 0; i < cs.length; i++) cs[i] = (Component)((GraphNode)sel[i]).get(gp.COMPONENT); int min; int size; if(space) switch(direction) { case HORIZONTAL: sort(cs, 0, cs.length - 1, CMP_CARD_L); size = 0; for(int i = 0; i < cs.length; i++) size += cs[i].getWidth(); min = CMP_CARD_L.card(cs[0]); size = CMP_CARD_R.card(cs[cs.length - 1]) - min - size; min += cs[0].getWidth(); for(int i = 1; i < cs.length - 1; i++) { cs[i].setLocation(((i * size) / (cs.length - 1)) + min, cs[i].getY()); min += cs[i].getWidth(); } break; case VERTICAL: sort(cs, 0, cs.length - 1, CMP_CARD_T); size = 0; for(int i = 0; i < cs.length; i++) size += cs[i].getHeight(); min = CMP_CARD_T.card(cs[0]); size = CMP_CARD_B.card(cs[cs.length - 1]) - min - size; min += cs[0].getHeight(); for(int i = 1; i < cs.length - 1; i++) { cs[i].setLocation(cs[i].getX(), ((i * size) / (cs.length - 1)) + min); min += cs[i].getHeight(); } break; } else switch(direction) { case LEFT: sort(cs, 0, cs.length - 1, CMP_CARD_L); min = CMP_CARD_L.card(cs[0]); size = CMP_CARD_L.card(cs[cs.length - 1]) - min; if(size > 0) for(int i = 1; i < cs.length - 1; i++) cs[i].setLocation(((i * size) / (cs.length - 1)) + min, cs[i].getY()); break; case HORIZONTAL: sort(cs, 0, cs.length - 1, CMP_CARD_H); min = CMP_CARD_H.card(cs[0]); size = CMP_CARD_H.card(cs[cs.length - 1]) - min; if(size > 0) for(int i = 1; i < cs.length - 1; i++) cs[i].setLocation(((i * size) / (cs.length - 1)) + min - cs[i].getWidth() / 2, cs[i].getY()); break; case RIGHT: sort(cs, 0, cs.length - 1, CMP_CARD_R); min = CMP_CARD_R.card(cs[0]); size = CMP_CARD_R.card(cs[cs.length - 1]) - min; if(size > 0) for(int i = 1; i < cs.length - 1; i++) cs[i].setLocation(((i * size) / (cs.length - 1)) + min - cs[i].getWidth(), cs[i].getY()); break; case TOP: sort(cs, 0, cs.length - 1, CMP_CARD_T); min = CMP_CARD_T.card(cs[0]); size = CMP_CARD_T.card(cs[cs.length - 1]) - min; if(size > 0) for(int i = 1; i < cs.length - 1; i++) cs[i].setLocation(cs[i].getX(), ((i * size) / (cs.length - 1) + min)); break; case VERTICAL: sort(cs, 0, cs.length - 1, CMP_CARD_V); min = CMP_CARD_V.card(cs[0]); size = CMP_CARD_V.card(cs[cs.length - 1]) - min; if(size > 0) for(int i = 1; i < cs.length - 1; i++) cs[i].setLocation(cs[i].getX(), ((i * size) / (cs.length - 1)) + min - cs[i].getHeight() / 2); break; case BOTTOM: sort(cs, 0, cs.length - 1, CMP_CARD_B); min = CMP_CARD_B.card(cs[0]); size = CMP_CARD_B.card(cs[cs.length - 1]) - min; if(size > 0) for(int i = 1; i < cs.length - 1; i++) cs[i].setLocation(cs[i].getX(), ((i * size) / (cs.length - 1)) + min - cs[i].getHeight()); break; } gp.repaint(); } void rotate(int angle){ Object o = ctxmgr.getLastObject(); if(o instanceof GraphNodeComponent) ((GraphNodeComponent)o).setRotation(((GraphNodeComponent)o).getRotation() + angle); } private static final int TO_FRONT = 0; private static final int FORWARD = 1; private static final int BACKWARD = 2; private static final int TO_BACK = 3; private void arrange(int key) { SetSelectionModel sm = gp.getSelectionModel(); Component[] cs = new Component[sm.size(GraphNode.class)]; int[] idxs = new int[cs.length]; Component[] cmps = gp.getComponents(); int j = 0; for(int i = 0; i < cmps.length; i++) if(sm.isSelected(gp.viewToModel(cmps[i]))) { idxs[j] = i; cs[j++] = cmps[i]; } switch(key) { case TO_FRONT: for(int i = j - 1; i >= 0; i--) { gp.remove(cs[i]); gp.add(cs[i], 1); } break; case FORWARD: for(int i = 0; i < j; i++) { gp.remove(cs[i]); int idx = idxs[i] - 1; if(idx < 1) idx = 1; gp.add(cs[i], idx); } break; case BACKWARD: for(int i = 0; i < j; i++) { gp.remove(cs[i]); int idx = idxs[i] + 1; if(idx > cmps.length) idx = cmps.length; gp.add(cs[i], idx); } break; case TO_BACK: for(int i = 0; i < j; i++) { gp.remove(cs[i]); gp.add(cs[i]); } break; } gp.repaint(); } private void addActions(Action[] actions) { ActionMap amap = getActionMap(); for(int i = 0; i < actions.length; i++) amap.put(actions[i].getValue(Action.NAME), actions[i]); } public Action[] getEditActions() { return editActions; } public Action[] getAlignActions() { return alignActions; } public Action[] getDistributeActions() { return distributeActions; } public Action[] getRotateActions() { return rotateActions; } public Action[] getArrangeActions() { return arrangeActions; } private Action[] alignActions = { new AbstractAction("Left", Resource.ALIGN_LEFT) { public void actionPerformed(ActionEvent e) {align(LEFT);} }, new AbstractAction("Horizontal", Resource.ALIGN_HCENTER) { public void actionPerformed(ActionEvent e) {align(HORIZONTAL);} }, new AbstractAction("Right", Resource.ALIGN_RIGHT) { public void actionPerformed(ActionEvent e) {align(RIGHT);} }, new AbstractAction("Top", Resource.ALIGN_TOP) { public void actionPerformed(ActionEvent e) {align(TOP);} }, new AbstractAction("Vertical", Resource.ALIGN_VCENTER) { public void actionPerformed(ActionEvent e) {align(VERTICAL);} }, new AbstractAction("Bottom", Resource.ALIGN_BOTTOM) { public void actionPerformed(ActionEvent e) {align(BOTTOM);} }, }; private Action[] arrangeActions = { new AbstractAction("Bring To Front", Resource.TO_FRONT) { public void actionPerformed(ActionEvent e) {arrange(TO_FRONT);} }, new AbstractAction("Bring Forward", Resource.FORWARD) { public void actionPerformed(ActionEvent e) {arrange(FORWARD);} }, new AbstractAction("Send Backward", Resource.BACKWARD) { public void actionPerformed(ActionEvent e) {arrange(BACKWARD);} }, new AbstractAction("Send To Back", Resource.TO_BACK) { public void actionPerformed(ActionEvent e) {arrange(TO_BACK);} }, }; private Action[] distributeActions = { new AbstractAction("Left", Resource.DISTRIBUTE_LEFT) { public void actionPerformed(ActionEvent e) {distribute(LEFT, false);} }, new AbstractAction("Horizontal", Resource.DISTRIBUTE_HCENTER) { public void actionPerformed(ActionEvent e) {distribute(HORIZONTAL, false);} }, new AbstractAction("Space Horizontal", Resource.DISTRIBUTE_SPACE_H) { public void actionPerformed(ActionEvent e) {distribute(HORIZONTAL, true);} }, new AbstractAction("Right", Resource.DISTRIBUTE_RIGHT) { public void actionPerformed(ActionEvent e) {distribute(RIGHT, false);} }, new AbstractAction("Top", Resource.DISTRIBUTE_TOP) { public void actionPerformed(ActionEvent e) {distribute(TOP, false);} }, new AbstractAction("Vertical", Resource.DISTRIBUTE_VCENTER) { public void actionPerformed(ActionEvent e) {distribute(VERTICAL, false);} }, new AbstractAction("Space Vertical", Resource.DISTRIBUTE_SPACE_V) { public void actionPerformed(ActionEvent e) {distribute(VERTICAL, true);} }, new AbstractAction("Bottom", Resource.DISTRIBUTE_BOTTOM) { public void actionPerformed(ActionEvent e) {distribute(BOTTOM, false);} }, }; private Action[] rotateActions = { new AbstractAction("90" + Unicode.deg, Resource.ROT_90) { public void actionPerformed(ActionEvent e) {rotate(GraphNodeComponent.ANGLE_90);} }, new AbstractAction("180" + Unicode.deg, Resource.ROT_180) { public void actionPerformed(ActionEvent e) {rotate(GraphNodeComponent.ANGLE_180);} }, new AbstractAction("270" + Unicode.deg, Resource.ROT_270) { public void actionPerformed(ActionEvent e) {rotate(GraphNodeComponent.ANGLE_270);} }, }; } /* $Log: GraphPanelEditor.java,v $ Revision 1.1 2002/07/11 12:09:52 ohitz Initial checkin Revision 1.6 2001/03/20 21:31:26 schubige improved sample soundlet, added arrange actions to graph panel editor Revision 1.5 2001/03/11 17:59:39 schubige Fixed various soundium and iiuf.swing.graph bugs Revision 1.4 2001/03/07 17:36:28 schubige soundium properties panel beta Revision 1.3 2001/03/05 17:55:07 schubige Still working on soundium properties panel Revision 1.2 2001/02/23 11:03:15 schubige try to recover table_source.png Revision 1.1 2001/02/17 09:54:21 schubige moved graph stuff to iiuf.swing.graph, started work on rotatable GraphNodeComponents Revision 1.12 2001/02/16 14:29:05 schubige Changed sound engine spec. Revision 1.11 2001/02/16 13:47:38 schubige Adapted soundium to new rtsp version Revision 1.10 2001/02/15 16:00:43 schubige Improved graph panel, fixed some soundium bugs Revision 1.9 2001/02/14 17:25:37 schubige implemented resizing, select all and key-shortcuts for graph panel Revision 1.8 2001/02/12 17:50:05 schubige still working on soundium gui Revision 1.7 2001/02/11 16:25:39 schubige working on soundium Revision 1.6 2001/02/09 17:34:16 schubige working on soundium Revision 1.5 2001/01/04 16:28:38 schubige Header update for 2001 and DIUF Revision 1.4 2000/12/29 08:03:55 schubige SourceWatch beta debug iter 1 Revision 1.3 2000/12/28 09:29:10 schubige SourceWatch beta Revision 1.2 2000/12/20 09:46:39 schubige TJGUI update Revision 1.1 2000/12/18 12:44:35 schubige Added ports to iiuf.util.graph */