/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Nomad is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.sf.nmedit.jtheme.component.plaf; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Component; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.image.BufferedImage; import java.lang.ref.SoftReference; import java.util.Collection; import java.util.HashMap; import java.util.Map; import javax.swing.JComponent; import javax.swing.SwingUtilities; import net.sf.nmedit.jpatch.PConnection; import net.sf.nmedit.jpatch.PConnectionManager; import net.sf.nmedit.jpatch.PConnector; import net.sf.nmedit.jpatch.PConnectorDescriptor; import net.sf.nmedit.jpatch.PSignal; import net.sf.nmedit.jpatch.PSignalTypes; import net.sf.nmedit.jpatch.history.PUndoableEditSupport; import net.sf.nmedit.jtheme.JTCursor; import net.sf.nmedit.jtheme.cable.Cable; import net.sf.nmedit.jtheme.cable.DragCable; import net.sf.nmedit.jtheme.component.JTComponent; import net.sf.nmedit.jtheme.cable.JTCableManager; import net.sf.nmedit.jtheme.component.JTConnector; import net.sf.nmedit.nmutils.Platform; import net.sf.nmedit.nmutils.graphics.RoundGradientPaint; public class JTBasicConnectorUI extends JTConnectorUI { private static UIInstance<JTConnectorUI> uiInstance = new UIInstance<JTConnectorUI>(JTConnectorUI.class); public static JTConnectorUI createUI(JComponent c) { JTConnectorUI ui = uiInstance.getInstance(c); if (ui == null) uiInstance.setInstance(c, ui = new JTBasicConnectorUI()); return ui; } public void paintDynamicLayer(Graphics2D g, JTComponent c) { JTConnector con; try { con = (JTConnector) c; } catch (ClassCastException e) { throw e; } PSignal signal = con.getSignal(); Color color = signal == null ? Color.BLACK : signal.getColor(); int size = diameter(c.getWidth(), c.getHeight()); paintConnector(g, size, color, con.isOutput(), con.isConnected()); } protected int getSize(JTConnector c) { int size = Math.min(c.getWidth(), c.getHeight()) -1; if (size % 2 == 0) size --; return size; } private static int diameter(int w, int h) { int d = Math.min(w, h); return d - (d+1)%2; } private static final Color OUTLINE = new Color(0,0,0,.5f); private static final Color RGP_INNER = new Color(0,0,0,.85f); private static final Color RGP_OUTER = new Color(0,0,0,.05f); protected static int computeHash(int size, Color fill, boolean output, boolean connected) { return ((fill.hashCode()*size)<<2)|(output?2:0)|(connected?1:0); } private static class CachedConnector { private int size; private Color fill; private boolean output; private boolean connected; private int hashCode; private Image image; public CachedConnector(int size, Color fill, boolean output, boolean connected, int hashCode, Image image) { this.size = size; this.fill = fill; this.output = output; this.connected = connected; this.hashCode = hashCode; this.image = image; } public int hashCode() { return hashCode; } public boolean equals(Object o) { if (o == null || o.hashCode() != hashCode) return false; if (o == this) return true; if (!(o instanceof CachedConnector)) return false; CachedConnector c = (CachedConnector) o; return equals(c.size, c.fill, c.output, c.connected); } public boolean equals(int size, Color fill, boolean output, boolean connected) { return size==this.size && fill.getRGB()==this.fill.getRGB()&&output==this.output&&connected==this.connected; } } private transient SoftReference<Map<Integer, CachedConnector>> cache ; protected Image getConnector(int size, Color fill, boolean output, boolean connected) { final int hash = computeHash(size, fill, output, connected); Map<Integer, CachedConnector> map = null; if (cache != null) map = cache.get(); if (map == null) { map = new HashMap<Integer, CachedConnector>(); cache = new SoftReference<Map<Integer,CachedConnector>>(map); } else { CachedConnector c = map.get(hash); if (c != null && c.equals(size, fill, output, connected)) { // found return c.image; } } // render new image BufferedImage bi = new BufferedImage(size, size, output ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = bi.createGraphics(); try { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); renderConnector(g2, size, fill, output, connected); } finally { g2.dispose(); } map.put(hash, new CachedConnector(size, fill, output, connected, hash, bi)); return bi; } protected void paintConnector(Graphics2D g, int s, Color fill, boolean output, boolean connected) { /* renderConnector(g, s, fill, output, connected); */ Image buf = getConnector(s, fill, output, connected); g.drawImage(buf, 0, 0, null); } protected void renderConnector(Graphics2D g, final int s, Color fill, boolean output, boolean connected) { int c = (s+1)/2; // (s mod 2) == 1 Color bright = fill.brighter().brighter(); g.setPaint(new GradientPaint(0, 0, fill, s, s, bright)); if (output) { g.fillRect(0, 0, s, s); g.setPaint(new GradientPaint(0, 0, bright, c, c, fill)); g.fillRect(0, 0, c, c); } else g.fillOval(0, 0, s, s); final int xy = (int)(s*3f/16f); c++; float c2 = c/2f; float sxy = xy+c2; g.setPaint(new RoundGradientPaint(sxy, sxy, c2, RGP_INNER, RGP_OUTER)); g.fillOval(xy, xy, c, c); if (connected) { g.setColor(fill); g.fillOval(xy, xy, c, c); } // outlines g.setColor(OUTLINE); if (output) g.drawRect(0, 0, s-1, s-1); else g.drawOval(0, 0, s-1, s-1); } private transient BasicConnectorListener connectorListenerInstance; protected BasicConnectorListener createConnectorListener(JComponent c) { if (connectorListenerInstance == null) connectorListenerInstance = new BasicConnectorListener(); return connectorListenerInstance; } protected BasicConnectorListener getConnectorListener(JComponent c) { MouseListener[] listeners = c.getListeners(MouseListener.class); if (listeners != null) { for (int i=0;i<listeners.length;i++) { MouseListener l = listeners[i]; if (l instanceof BasicConnectorListener) { return (BasicConnectorListener) l; } } } return null; } public void installUI(JComponent c) { c.setOpaque(false); c.setCursor(JTCursor.getJackCursor(JTCursor.JACK1)); // TODO this should be defined somewhere else c.setFocusable(true); BasicConnectorListener listener = createConnectorListener(c); if (listener != null) { listener.installListener(c); } } public void uninstallUI(JComponent c) { c.setCursor(null); BasicConnectorListener listener = getConnectorListener(c); if (listener != null) { listener.uninstallListener(c); } } public static class BasicConnectorListener implements MouseListener, MouseMotionListener, FocusListener { public void installListener(JComponent c) { installMouseListener(c); installMouseMotionListener(c); installFocusListener(c); } protected void installMouseMotionListener(JComponent c) { c.addMouseMotionListener(this); } protected void installMouseListener(JComponent c) { c.addMouseListener(this); } public void installFocusListener(JComponent c) { //c.addFocusListener(this); } protected void uninstallMouseMotionListener(JComponent c) { c.removeMouseMotionListener(this); } protected void uninstallMouseListener(JComponent c) { c.removeMouseListener(this); } public void uninstallFocusListener(JComponent c) { //c.removeFocusListener(this); } public void uninstallListener(JComponent c) { uninstallMouseListener(c); uninstallMouseMotionListener(c); uninstallFocusListener(c); } public void mouseDragged(MouseEvent e) { JTConnector c = (JTConnector) e.getComponent(); if (pressClickCount == 1) { if (!isDragging()) { startDragNewCable(c, e.getX(), e.getY()); } } else if (pressClickCount == 2) { if (!isDragging()) { startDragCurrentCables(c, e.getX(), e.getY()); } } updateDragLocation(c, e.getX(), e.getY()); } public void mouseMoved(MouseEvent e) { // TODO Auto-generated method stub } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub } private int pressClickCount = 0; public void mousePressed(MouseEvent e) { pressClickCount = 0; JTConnector c = (JTConnector) e.getComponent(); if (!c.hasFocus()) c.requestFocus(); if (!Platform.isLeftMouseButtonOnly(e)) return ; pressClickCount = e.getClickCount(); } public void mouseReleased(MouseEvent e) { pressClickCount = 0; stopDrag((JTConnector) e.getComponent(), e.getX(), e.getY()); } public void focusGained(FocusEvent e) { e.getComponent().repaint(); } public void focusLost(FocusEvent e) { e.getComponent().repaint(); } public void startDragNewCable(JTConnector c, int x, int y) { if (isDragging()) return; dragCreate = true; JTCableManager cableManager = c.getCableManager(); if (cableManager != null && cableManager.getView() != null) { dragSource = c; DragCable drag = new DragCable(cableManager.createCable(c, null)); PConnectorDescriptor cd = c.getConnectorDescriptor(); if (cd != null) { PSignalTypes signalTypes = cd.getParentDescriptor().getModules().getDefinedSignals(); PSignal noSignal = signalTypes.noSignal(); if (noSignal != null && noSignal.getColor() != null) drag.setColor(noSignal.getColor()); } cables = new Cable[] {drag}; cableManager.add(drag); drag.setStartLocation(c); updateDragLocation(c, x, y); } } protected JTConnector findConnectorAt(JTConnector otherConnector, int modContX, int modContY) { Component c = otherConnector.getParent().getParent().findComponentAt(modContX, modContY); if (c instanceof JTConnector) return (JTConnector)c; return null; } protected void updateDragLocation(JTConnector c, int x, int y) { if (!isDragging()) return ; JTCableManager cableManager = c.getCableManager(); if (cableManager == null) return; Point stop = SwingUtilities.convertPoint(c, new Point(x, y), cableManager.getOwner()); JTConnector target = findConnectorAt(c, stop.x, stop.y); if (target != null) DragCable.setLocation(stop, target); if (connectedCables != null) { cableManager.update(connectedCables); for (Cable cable:connectedCables) { Point cstart = cable.getStart(); Point cstop = cable.getStop(); if (cable.getSource() == c.getConnector()) { cstart.setLocation(stop); } else { cstop.setLocation(stop); } cable.setEndPoints(cstart, cstop); } cableManager.update(connectedCables); scrollToVisible(cableManager.getOwner(), c, x, y); return; } cableManager.update(cables); for (int i=cables.length-1;i>=0;i--) { Cable cable = cables[i]; cable.setEndPoints(cable.getStart(), new Point(stop)); } cableManager.update(cables); scrollToVisible(cableManager.getOwner(), c, x, y); } private void scrollToVisible(JComponent view, JComponent c, int x, int y) { Point p = new Point(x, y); p = SwingUtilities.convertPoint(c, p, view); Rectangle r = new Rectangle(p.x-10, p.y-10, 20, 20); SwingUtilities.computeIntersection(0, 0, view.getWidth(), view.getHeight(), r); view.scrollRectToVisible(r); } private transient Cable[] connectedCables; public void startDragCurrentCables(JTConnector c, int x, int y) { if (isDragging()) return; connectedCables = c.getConnectedCables(); if (connectedCables.length>0) dragSource = c; else connectedCables = null; } public void stopDrag(JTConnector c, int x, int y) { if (!isDragging()) return ; dragSource = null; JTCableManager cableManager = c.getCableManager(); Point stop = SwingUtilities.convertPoint(c, new Point(x, y), cableManager.getOwner()); JTConnector target = findConnectorAt(c, stop.x, stop.y); if (connectedCables != null) { PConnectionManager cm = c.getConnector().getConnectionManager(); cableManager.update(connectedCables); for (Cable cable: connectedCables) { cable.updateEndPoints(); } cableManager.update(connectedCables); PUndoableEditSupport ues = c.getConnector().getEditSupport(); try { if (ues != null) ues.beginUpdate(); if (target != null) { Collection<PConnection> cc = cm.connections(c.getConnector()); cm.removeAllConnections(cc); for (PConnection con: cc) { PConnector b = con.getA(); if (c.getConnector() == b) b = con.getB(); b.connect(target.getConnector()); } } else { // disconnect cables connected to this connector c.getConnector().disconnect(); } } finally { if (ues != null) ues.endUpdate(); } connectedCables = null; cables = JTConnector.NO_CABLES; //System.out.println("X not deleted(cables=):"+cables.length); return; } if (cableManager != null) { cableManager.remove(cables); } if (target != null) { if (dragCreate) { PConnector a = c.getConnector(); PConnector b = target.getConnector(); if (a == null && b == null) { Cable cable = cableManager.createCable(c, target); if (cables.length == 1) cable.setShake(cables[0].getShake()); cableManager.add(cable); } else if (a != null && b != null) { // TODO create new cable with same 'shake' value a.getConnectionManager().add(a, b); } } } cables = JTConnector.NO_CABLES; } public boolean isDragging() { return dragSource != null; } private JTConnector dragSource; private Cable[] cables = JTConnector.NO_CABLES; private boolean dragCreate = true; } }