/******************************************************************************** * * * (c) Copyright 2010 Verizon Communications USA and The Open University UK * * * * This software is freely distributed in accordance with * * the GNU Lesser General Public (LGPL) license, version 3 or later * * as published by the Free Software Foundation. * * For details see LGPL: http://www.fsf.org/licensing/licenses/lgpl.html * * and GPL: http://www.fsf.org/licensing/licenses/gpl-3.0.html * * * * This software is provided by the copyright holders and contributors "as is" * * and any express or implied warranties, including, but not limited to, the * * implied warranties of merchantability and fitness for a particular purpose * * are disclaimed. In no event shall the copyright owner or contributors be * * liable for any direct, indirect, incidental, special, exemplary, or * * consequential damages (including, but not limited to, procurement of * * substitute goods or services; loss of use, data, or profits; or business * * interruption) however caused and on any theory of liability, whether in * * contract, strict liability, or tort (including negligence or otherwise) * * arising in any way out of the use of this software, even if advised of the * * possibility of such damage. * * * ********************************************************************************/ package com.compendium.ui.plaf; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; import com.compendium.core.ICoreConstants; import com.compendium.ui.UILine; /** * The class which is the base class for handles the drawing of lines. * * @author Ron van Hoof / Michelle Bachler */ public class LineUI extends ComponentUI implements MouseListener, MouseMotionListener, KeyListener { protected final static int ARROW_ORIENTATION_FREE = 0; protected final static int ARROW_ORIENTATION_VERTICAL = 1; protected final static int ARROW_ORIENTATION_HORIZONTAL = 2; /** The selection color used byt his UI for painting selected lines (links).*/ private static final Color SELECTED_COLOR = Color.yellow; /** The UILine instance associated with this UI.*/ protected UILine oLine; /** The MouseListener used by this UI.*/ private MouseListener oMouseListener; /** The MouseMotionListener used by this UI.*/ private MouseMotionListener oMouseMotionListener; /** The KeyListener use by this UI.*/ private KeyListener oKeyListener; /** * Create a new LineUI instance. * @param c, the component this is the ui to install for. */ public static ComponentUI createUI(JComponent c) { return new LineUI(); } /** * Run any install instructions for installing this UI. * @param c, the component this is the ui for. */ public void installUI(JComponent c) { super.installUI(c); oLine = (UILine)c; installListeners(c); } /** * Install any Listener classes required by this UI. * @param c, the component to install the listeners for. */ protected void installListeners(JComponent c) { if ( (oMouseListener = createMouseListener( c )) != null ) { c.addMouseListener( oMouseListener ); } if ( (oMouseMotionListener = createMouseMotionListener( c )) != null ) { c.addMouseMotionListener( oMouseMotionListener ); } if ( (oKeyListener = createKeyListener( c )) != null ) { c.addKeyListener( oKeyListener ); } } /** * Just returns this class as the MouseListener. * @param c, the component to create the MouseLisener for. * @return MouseListener, the listener to use. */ protected MouseListener createMouseListener( JComponent c ) { return this; } /** * Just returns this class as the MouseMotionListener. * @param c, the component to create the MouseMotionLisener for. * @return MouseMotionListener, the listener to use. */ protected MouseMotionListener createMouseMotionListener( JComponent c ) { return this; } /** * Just returns this class as the KeyListener. * @param c, the component to create the KeyLisener for. * @return KeyListener, the listener to use. */ protected KeyListener createKeyListener(JComponent c) { return this; } /** * Run any uninstall instructions for uninstalling this UI. * @param c, the component this is the ui to uninstall for. */ public void uninstallUI(JComponent c) { uninstallListeners(c); oLine = null; super.uninstallUI(c); } /** * Uninstall any Listener classes used by this UI. * @param c, the component to uninstall the listeners for. */ protected void uninstallListeners(JComponent c) { if ( oMouseMotionListener!= null ) { c.removeMouseMotionListener( oMouseMotionListener ); } if ( oMouseListener!= null ) { c.removeMouseListener( oMouseListener ); } oMouseListener = null; oMouseMotionListener = null; } /** * Draws the line on the given graphics context. * * @param g, the Graphics object for this pain method to use. * @param c, the component to paint. */ public void paint(Graphics g, JComponent c) { UILine line = null; if (c instanceof UILine) { line = (UILine)c; } else { return; } // get from and to points Point from = line.getFrom(); Point to = line.getTo(); //-------- // draw the line //Integer fromX = new Integer(from.x); //Integer fromY = new Integer(from.y); //Integer toX = new Integer(to.x); //Integer toY = new Integer(to.y); //log.info("Drawing line (" + fromX.toString() + ", " + fromY.toString()+ ")---> (" + toX.toString() + ", " + toY.toString()+ ")\n"); //--------- // if one of the points is missing don't draw line if (from == null || to == null) return; // determine relative to and from points Point f = new Point(); Point t = new Point(); if (oLine.getCoordinateType() == UILine.RELATIVE) { // coordinates already relative to this components coordinate system f = from; t = to; } else { // calculate the relative coordinates by converting the coordinates from // the parents coordinate system to this components coordinate system Container parent = line.getParent(); if (parent != null) { f = SwingUtilities.convertPoint(parent, from, line); t = SwingUtilities.convertPoint(parent, to, line); } else { return; } } // set color of line if (line.isSelected()) g.setColor(line.getSelectedColor()); else g.setColor(line.getForeground()); // draw the line /* Integer fromX = new Integer(f.x); Integer fromY = new Integer(f.y); Integer toX = new Integer(t.x); Integer toY = new Integer(t.y); log.info("Drawing line (" + fromX.toString() + ", " + fromY.toString()+ ")---> (" + toX.toString() + ", " + toY.toString()+ ")\n"); */ g.drawLine(f.x, f.y, t.x, t.y); // draw arrow(s) if necessary switch (line.getArrow()) { case ICoreConstants.NO_ARROW: { break; } case ICoreConstants.ARROW_TO: { drawArrow(g, f, t, line.getCurrentArrowHeadWidth()); break; } case ICoreConstants.ARROW_FROM: { drawArrow(g, t, f, line.getCurrentArrowHeadWidth()); break; } case ICoreConstants.ARROW_TO_AND_FROM: { drawArrow(g, f, t, line.getCurrentArrowHeadWidth()); drawArrow(g, t, f, line.getCurrentArrowHeadWidth()); break; } } if (line.isRollover()) paintRollover(g,c); } // paint // Nice barbed type arrow heads, but needs tweaking for size and link style /*private static int yCor(int len, double dir) {return (int)(len * Math.cos(dir));} private static int xCor(int len, double dir) {return (int)(len * Math.sin(dir));} public static void drawArrow(Graphics2D g2d, int xCenter, int yCenter, int x, int y, float stroke) { double aDir=Math.atan2(xCenter-x,yCenter-y); Polygon tmpPoly=new Polygon(); int i1=12+(int)(stroke*2); int i2=6+(int)stroke; // make the arrow head the same size regardless of the length length tmpPoly.addPoint(x,y); // arrow tip tmpPoly.addPoint(x+xCor(i1,aDir+.5),y+yCor(i1,aDir+.5)); tmpPoly.addPoint(x+xCor(i2,aDir),y+yCor(i2,aDir)); tmpPoly.addPoint(x+xCor(i1,aDir-.5),y+yCor(i1,aDir-.5)); tmpPoly.addPoint(x,y); // arrow tip g2d.drawPolygon(tmpPoly); g2d.fillPolygon(tmpPoly); // remove this line to leave arrow head unpainted }*/ /** * CURRENLTY DOES NOTHING. */ public void paintRollover(Graphics g, JComponent c) {} // paintRollover protected void drawArrow (Graphics g, Point a, Point b, int width, int orientation, int lineWidth) { int unitx; int unity; int xpts[] = new int[3]; int ypts[] = new int[3]; xpts[0]=b.x; ypts[0]=b.y; if (orientation == ARROW_ORIENTATION_FREE) { float ratioWidth = (width)*(lineWidth); //CORSAIRE_EVOL previously only (width) was used if (ratioWidth > 4*width) { ratioWidth = 4*width; // avoid useless disproportion } double hypo = Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); unitx = (int)(ratioWidth*(b.x-a.x)/hypo); unity = (int)(ratioWidth*(b.y-a.y)/hypo); if (Math.abs(unitx) > 4*lineWidth) { unitx = (int)(4*lineWidth*Math.signum(unitx*1.0)); } if (Math.abs(unity) > 4*lineWidth) { unity = (int)(4*lineWidth*Math.signum(unity*1.0)); } xpts[1]=b.x-(int)(unitx-unity/2); ypts[1]=b.y-(int)(unity+unitx/2); xpts[2]=b.x-(int)(unitx-unity/2+unity); ypts[2]=b.y-(int)(unity+unitx/2-unitx); //log.info("Drawing arrow (" + xpts[0] + ", " + ypts[0] + ")---> (" + xpts[1] + ", �� // + ypts[1] + ")---> (" + xpts[2] + ", " + ypts[2] + ")\n"); // Start Code by Corsaire } else if (orientation == ARROW_ORIENTATION_HORIZONTAL) { // the arrow's basis is equals 4 * (the connected line width) // the sign is useful to set the arrow's nose in the right direction if ((b.x-a.x) > 0) unitx = 4*lineWidth; else unitx = -4*lineWidth; // Y orientation is symetric, so no sign to take into account unity = 2*lineWidth; xpts[1]=b.x-unitx; ypts[1]=b.y-unity; xpts[2]=b.x-unitx; ypts[2]=b.y+unity; } else if (orientation == ARROW_ORIENTATION_VERTICAL) { // X orientation is symetric, so no sign to take into account unitx = 2*lineWidth; // the arrow's basis is equals 4 * (the connected line width) // the sign is useful to set the arrow's nose in the right direction if ((b.y-a.y) > 0) unity = 4*lineWidth; else unity = -4*lineWidth; xpts[1]=b.x-unitx; ypts[1]=b.y-unity; xpts[2]=b.x+unitx; ypts[2]=b.y-unity; } g.drawPolygon(xpts,ypts,3); g.fillPolygon(xpts,ypts,3); // End Code by Corsaire } /** * Draws a one arrow at the end of the line. The line is given by two points. * The arrow is given by its width. * * @param g the Graphics object to use for drawing. * @param a one end pf the line. * @param b the other end of the line. * @param width the width of the arrow head to draw. */ protected void drawArrow(Graphics g, Point a, Point b, int width) { double hypo; int unitx; int unity; int xpts[]; int ypts[]; hypo=Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); unitx=(int)(width*(b.x-a.x)/hypo); unity=(int)(width*(b.y-a.y)/hypo); xpts=new int[3]; ypts=new int[3]; xpts[0]=b.x; ypts[0]=b.y; xpts[1]=b.x-unitx-unity/2; ypts[1]=b.y-unity+unitx/2; xpts[2]=b.x-unitx-unity/2+unity; ypts[2]=b.y-unity+unitx/2-unitx; //log.info("Drawing arrow (" + xpts[0] + ", " + ypts[0] + ")---> (" + xpts[1] + ", " + ypts[1] + ")---> (" + xpts[2] + ", " + ypts[2] + ")\n"); g.drawPolygon(xpts,ypts,3); g.fillPolygon(xpts,ypts,3); } /** * Return the preferred size of this component. * @param c, the component to return the preferred size for. * @return Rectangle, the preferred size of this component. */ public Dimension getPreferredSize(JComponent c) { UILine line = (UILine)c; Point from = line.getFrom(); Point to = line.getTo(); // if one of the points is missing no line could be drawn if (from == null || to == null) return new Dimension(0,0); int width = Math.abs(from.x - to.x); int height = Math.abs(from.y - to.y); // make sure that the width and height are large enough // to fit a possible arrow, the line thickness and takes into account the minimum width int w = Math.max(line.getLineThickness(), line.getMinWidth()); if ((line.getArrow() != ICoreConstants.NO_ARROW) && (line.getCurrentArrowHeadWidth() > w)) w = line.getCurrentArrowHeadWidth(); width += w; height += w; return new Dimension(width, height); } /** * Return the minimum size of this component. * @param c, the component to return the minimum size for. * @return Rectangle, the minimum size of this component. * @see #getPreferredSize */ public Dimension getMinimumSize(JComponent c) { return getPreferredSize(c); } /** * Return the maximum size of this component. * @param c, the component to return the maximum size for. * @return Rectangle, the maximum size of this component. * @see #getPreferredSize */ public Dimension getMaximumSize(JComponent c) { return getPreferredSize(c); } /** * Return the prefrerred bounds of this component. * @param c, the component to return the preferred bounds for. * @return Rectangle, the preferred bounds of this component. */ public Rectangle getPreferredBounds(JComponent c) { UILine line = (UILine)c; Point from = line.getFrom(); Point to = line.getTo(); if (from == null || to == null) return new Rectangle(0,0,0,0); Dimension size = getPreferredSize(c); // determine absolute to and from points in parent's coordinate system Point f = new Point(0,0); Point t = new Point(0,0); if (oLine.getCoordinateType() == UILine.ABSOLUTE) { // coordinates already relative to this components coordinate system f = from; t = to; } else { // calculate the absolute coordinates by converting the coordinates from // this components coordinate system to the parents coordinate system Container parent = line.getParent(); if (parent != null) { f = SwingUtilities.convertPoint(line, from, parent); t = SwingUtilities.convertPoint(line, to, parent); } } int x = Math.min(f.x, t.x); int y = Math.min(f.y, t.y); // give room for possible arrow and/or line thickness int w = Math.max(line.getLineThickness(), line.getMinWidth()); if ((line.getArrow() != ICoreConstants.NO_ARROW) && (line.getCurrentArrowHeadWidth() > w)) w = line.getCurrentArrowHeadWidth(); x -= w/2; y -= w/2; return new Rectangle(x, y, size.width, size.height); } /***** EVENT MOUSE HANDLING METHODS *****/ /** * Invoked when a mouse is pressed. * @param evt, the MouseEvent generated. */ public void mousePressed(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a mouse is clicked. * @param evt, the MouseEvent generated. */ public void mouseClicked(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a mouse is released. * @param evt, the MouseEvent generated. */ public void mouseReleased(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a mouse is entered. * @param evt, the MouseEvent generated. */ public void mouseEntered(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a mouse is exited. * @param evt, the MouseEvent generated. */ public void mouseExited(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a mouse is dragged (pressed and moved). * @param evt, the MouseEvent generated. */ public void mouseDragged(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a mouse is moved in a component. * @param evt, the MouseEvent generated. */ public void mouseMoved(MouseEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a key is pressed in a component. * @param evt, the MouseEvent generated. */ public void keyPressed(KeyEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a key is released in a component. * @param evt, the MouseEvent generated. */ public void keyReleased(KeyEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } /** * Invoked when a key is typed in a component. * @param evt, the MouseEvent generated. */ public void keyTyped(KeyEvent evt) { if (oLine != null) oLine.getParent().dispatchEvent(evt); } }