// Medusa is a graph viewer which allows interactive editing of networks
// (edges and nodes) and also connects to databases.
//
// Copyright (C) 2006 Sean Hooper
//
// 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 2 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, write to the 
// Free Software Foundation, Inc., 
// 59 Temple Place, Suite 330, 
// Boston, MA 02111-1307 USA
package medusa.display;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JColorChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import medusa.*;
import medusa.dataio.DataLoader;
import medusa.graph.Edge;
import medusa.graph.Graph;
import medusa.graph.Node;



/**
 * Describe class DisplayPanel here.
 * This class extends BasicGraphPanel and allows mouse interactions,
 * and also data manipulation.
 *
 * @author Sean Hooper
 * @version 1.0
 */
public class DisplayPanel extends BasicGraphPanel {
    
    
    //Image image=null;
    BufferedImage image=null;
    Image tempImage=null;
    // constructor
    /**
     * Constructor with settings
     * @param stringSettings settings
     */
    public DisplayPanel(MedusaSettings stringSettings){
        super(stringSettings);
        initPopup();
        tryImage();
        initShow();
        //setBackground(Color.grey);
    }
    
    /**
     * Constructor with default settings
     */
    public DisplayPanel(){
        super();
        initPopup();
        tryImage();
        initShow();
    }
    
    private void tryImage(){
        try {
            setImage();
            tempImage=image;
        } catch (Exception e){}
    }
    
    /**
     * Sets the default image
     * @throws java.io.IOException if default file
     */
    public void setImage() throws IOException{
        setImage("netview_default.png");
    }
    
    /**
     * Sets the background image
     * @param imageURL path to image
     * @throws java.io.IOException if image is not found or cannot be read.
     */
    public void setImage(final String imageURL)
    throws IOException{
        //System.out.println(imageURL);
        File imageFile=new File(imageURL);
        image=javax.imageio.ImageIO.read
                (imageFile);
        
    }
    
    
    
    /**
     *
     * @param myImage
     * @throws java.lang.InterruptedException
     */
    public void setImage(BufferedImage myImage)throws InterruptedException{
        image=myImage;
        //scaledCopy();
    }
    public void clearImage(){
        image=null;
        //System.out.println("image cleared");
    }
    
    
    /* -------------------------------------------------------------
     * -more overridden methods of BasicGraphPanel-------------------
     * -------------------------------------------------------------
     */
    
    public void drawBackgroundImage(Graphics2D g2d){
        if (image==null)
            return;
        g2d.drawImage(image, 0,0, getPanelWidth(), getPanelHeight(), null);
    }
    
    
    public void paintEdge(Graphics2D g, Edge e){
        if (!showEdges[e.getType()-1])
            return;
        //    return;
        Node from =getGraph().getNode(e.getFromName());
        Node to = getGraph().getNode(e.n2);
        int x1 = (int)from.getX();
        int y1 = (int)from.getY();
        int x2 = (int)to.getX();
        int y2 = (int)to.getY();
        
        // handle confidence
        if (showConfidence){
            if (alpha)
                g.setComposite(makeComposite(e.getConf()));
            else
                g.setStroke(getStroke(e.getConf()) );
            
        }
        g.setPaint(basicEdgeColor);
        //if (e.visible)
        //g.setPaint(Color.red);
        if (pretty){
            g.setPaint(getColor( new Integer(e.getType())  ));
            //System.out.println(stringSettings.getLabel(e.color));
            
            PaintTools.paintPath(g, x1, y1,
                    x2, y2, e.getOrientation(), 0.3, arrow);
        } else{
            
            g.drawLine(x1,y1,x2,y2);
        }
    }
    
    
    public boolean showBorder=true;
    public void setShowBorder(boolean show){
        showBorder=show;
    }
    
    public void paintNet(Graphics2D g2d){
        // if we have an image, paint it
        drawBackgroundImage(g2d);
        // draw the border
        //g2d.setColor(Color.gray);
        if (showBorder){
            Rectangle2D.Double border = new Rectangle2D.Double(2,2, getPanelWidth(), getPanelHeight());
            g2d.draw(border);
        }
        
        for (Iterator edges = graph.edgesIterator(); edges.hasNext() ;) {
            Edge e = (Edge) edges.next();
            int col=e.getType();
            if (showEdges[col-1])
                paintEdge(g2d, e);
        }
        
        
        //make sure confidence is restored
        g2d.setComposite(makeComposite(1.0f));
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            paintNode(g2d, node);
        }
        //PaintTools.paintPath(g2d,10);
    }
    
    public void saveImage(String path, int param){
        setShowBorder(false);
        super.saveImage(path,param);
        setShowBorder(true);
    }
    
    
    /**
     * Paints the component
     * @param g current graphics
     */
    public synchronized void paintComponent(Graphics g) {
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(1));
        
        clear(g2d);
        //paintBackground(g2d);clear(g2d);
        
        if (hideWhenMove){
            if ((!running)&(pick!=null)){
                //System.out.println("moving few");
                //g2d.drawString("weng",100,100);
                paintPick(g2d);
            } else {
                paintNet(g2d);
            }
        } else {
            paintNet(g2d);
        }
        
        //paintNet(g2d);
        //markNode(g2d, marked);
        // for selection boxes
        drawBox(g2d);
        if (drawTempLine){
            float[] dashPattern = { 2, 3, 2, 3 };
            g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER, 10,
                    dashPattern, 0));
            g2d.drawLine(startx,starty,endx,endy);
        }
        g2d.setStroke(new BasicStroke(1));
        
    }
    
    //public void loadingMessage(){
    //	Graphics2d g2d =
    
    protected void drawBox(Graphics2D g2d){
        if (getCurrentRect() != null) {
            //Draw a rectangle on top of the image.
            g2d.setXORMode(Color.red); //Color of line varies
            //depending on image colors
            float[] dashPattern = { 2, 3, 2, 3 };
            g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_MITER, 10,
                    dashPattern, 0));
            g2d.drawRect(rectToDraw.x, rectToDraw.y,
                    rectToDraw.width - 1, rectToDraw.height - 1);
        }
        
    }
    
    protected boolean showName=true;
    /**
     * Switches between showing node name and annotation
     * @param b true if names are to be shown, false if annotation
     */
    public void setShowName(boolean b){
        showName=b;
        showAnnotation=(!b);
    }
    
    public void paintNode(Graphics2D g, Node n){
        g.setStroke(new BasicStroke(1));
        g.setPaint(n.getColor());
        int x=(int) n.getX() - correct;
        int y=(int) n.getY() - correct;
        
        Shape shape = PaintTools.getShape(n.getShape(),x,y,nodeSize);
        g.fill(shape);
        g.setPaint(Color.black);
        if (n.isFixed())
            g.setPaint(Color.yellow);
        g.draw(shape);
        g.setPaint(fontColor);
        g.setFont(nodeFont);
        if (label){
            if (showName) {
                g.drawString(n.getLabel(),x-2,y-2);
            } else {
                g.drawString(n.getAnnotation(),x-2,y-2);
            }
        }
        
    }
    
    /**********************
     * // handle editing events (right mouse button)
     ************************/
    private void handleRightMouseDragged(MouseEvent e){
        if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) !=0){
            
            // set line drawing to true, and repaint
            drawTempLine=true;
            endx=e.getX();
            endy=e.getY();
            repaint();
        }
        
        e.consume();
    }
    public void mouseDragged(MouseEvent e) {
        handleLeftMouseDragged(e);
        handleRightMouseDragged(e);
        
    }
    public void handleReleaseRightButton(MouseEvent e){
        if (e.getButton()!=MouseEvent.BUTTON3)
            return;
        removeMouseMotionListener(this);
        //int i=getClosest(e);
        Node node = getClosest(e);
        if (node != null){
            // prompt for new node
            temp2 = node;
        } else
            temp2 = new Node("unnamed",e.getX(),e.getY());
        drawTempLine=false;
        endx=e.getX();
        endy=e.getY();
        if ( temp1!=temp2 ){
            //System.out.println(temp1+" "+temp2);
            addEdge();
            repaint();
            temp1=null;
            temp2=null;
        } else{
            //System.out.println(temp1.getLabel());
            showPopup(e);
        }
        
        //System.out.println(temp1+"\n ---> "+temp2);
        
    }
    
    public void mouseReleased(MouseEvent e){
        handleReleaseLeftButton(e);
        handleReleaseRightButton(e);
    }
    
    private void handlePressRightButton(MouseEvent e){
        if (e.getButton()==MouseEvent.BUTTON3){
            
            addMouseMotionListener(this);
            Node node = getClosest(e);
            //int index=getClosest(e);
            if (node != null){
                temp1 = node;
                //System.out.println("dragging from "+nodeArray[index]);
                startx = (int)temp1.getX();
                starty = (int)temp1.getY();
            } else {
                startx = e.getX();
                starty = e.getY();
                temp1=new Node("unnamed",startx,starty);
            }
            
            e.consume();
        }
    }
    
    public void mousePressed(MouseEvent e){
        
        handlePressLeftButton(e);
        handlePressRightButton(e);
    }
    
    // the edge dialog
    Frame f = new Frame();
    protected void addEdge(){
        //Frame f = new Frame();
        //System.out.println(temp1+" "+temp2);
        Edge e = EdgeDialog.showDialog(f,null,temp1,temp2,getStringSettings());
        if (e!=null){
            temp1.setLabel(e.getFromName());
            
            temp2.setLabel(e.getToName());
            
            graph.addEdge(e);
            graph.setNode(temp1);
            graph.setNode(temp2);
            updateNodes();
        }
    }
    
    // to add edges, we need startx, starty, endx and endy. When complete,
    // open the dialog to specify the params
    int startx;
    int starty;
    int endx;
    int endy;
    boolean drawTempLine=false;
    Node temp1=null;
    Node temp2=null;
    
    // an array to remember which edges to show/hide
    boolean[] showEdges;
    
    public void initShow(){
        final int max=20;
        showEdges=new boolean[max];
        for (int i=0; ii
     * @param i interaction type to show
     * @param b true: paint interaction type. false: hide interaction type
     */
    public void setShowEdge(int i, boolean b){
        showEdges[i]=b;
    }
    public void setHideEdge(int i, boolean b){
        showEdges[i]=(!b);
    }
    
    int border=10;
    
    /**
     * Sets the shape for fixed nodes
     * @param shape 0-3: implemented shapes in PaintTools
     */
    public void setShape(int shape){
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            if (node.isFixed()){
                node.setShape(shape);
            }
        }
    }
    
    
    /* popup!!!!!
     *
     */
    javax.swing.JPopupMenu popup;
    javax.swing.JMenuItem removeItem;
    javax.swing.JMenuItem editColorItem;
    javax.swing.JMenuItem editShapeItem;
    javax.swing.JMenuItem setAnnotationItem;
    javax.swing.JMenuItem getPositionItem;
    
    // show a popup in certain cases
    private void initPopup(){
        //System.out.println("initializing popup");
        popup = new javax.swing.JPopupMenu("Edit data");
        removeItem = new javax.swing.JMenuItem("Delete");
        editColorItem = new javax.swing.JMenuItem("Color");
        editShapeItem = new javax.swing.JMenuItem("Shape");
        setAnnotationItem = new javax.swing.JMenuItem("Annotation");
        getPositionItem = new javax.swing.JMenuItem("Position");
        removeItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent e) {
                if (temp1!=null){
                    //System.out.println("removing node "+temp1.getLabel());
                    removeEdgeByLabel(temp1.getLabel());
                    temp1=null;
                    temp2=null;
                    repaint();
                } else
                    System.out.println("node is null, for some strange reason");
            }
        } );
        editColorItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent e) {
                Color newColor = javax.swing.JColorChooser.showDialog(getRootPane(),
                        "Choose node color",
                        temp1.getColor());
                if (newColor!=null){
                    temp1.setColor(newColor);
                    
                    
                }
                repaint();
            }
        } );
        
        editShapeItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent e) {
                
                temp1.setShape(chooseShape());
                repaint();
            }
        } );
        setAnnotationItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent e) {
                setAnnotation(temp1);
            }
        } );
        getPositionItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent e) {
                getPosition(temp1);
            }
        } );
        popup.add(removeItem);
        popup.addSeparator();
        popup.add(editColorItem);
        popup.add(editShapeItem);
        popup.add(setAnnotationItem);
        popup.add(getPositionItem);
        
    }
    
    private void getPosition(Node temp){
        javax.swing.JOptionPane.showMessageDialog(this,
                "x: "+temp.getX()+" y:"+temp.getY());
    }
    
    private void setAnnotation(Node temp){
        String s = (String)javax.swing.JOptionPane.showInputDialog
                ("View or edit node annotation:\n",
                temp.getAnnotation());
        if ((s != null) && (s.length() > 0)) {
            temp.setAnnotation(s);
        }
    }
    
    private int chooseShape(){
        Object[] shapes = {"circle","rectangle","triangle","diamond"};
        int s =javax.swing.JOptionPane.showOptionDialog
                (this,
                "Choose a node shape",
                "Shape",
                javax.swing.JOptionPane.YES_NO_CANCEL_OPTION,
                javax.swing.JOptionPane.QUESTION_MESSAGE,
                null,
                shapes,
                "circle");
        return s;
    }
    
    public void updateStringSettings(MedusaSettings ss){
        setStringSettings(ss);
//        this.stringSettings=ss;
    }
    
    private void showPopup(MouseEvent e){
        popup.show(e.getComponent(), e.getX(), e.getY());
        e.consume();
    }
    
    /*
     * manipulate data
     *
     */
    public java.util.ArrayList getFixed(){
        return graph.getFixed();
    }
    public void copyNodeSettings(Graph g){
        graph.copyNodeSettings(g);
    }
    public void removeEdgeByLabel(String label){
        //System.out.println("removing "+label);
        graph.removeEdgeByLabel(label);
    }
    
    public void removeFixedNodes(){
        graph.removeFixed();
    }
    
    public void crop(double confidence){
        graph.cropEdges(confidence);
        calculateEdgeLength();
    }
   /* A regular expression search, for finding nodes matching a
    * string. The string should be sent from the front-end.
    
    */
    
    /**
     * Finds the node matching the regular expression
     * @param expression pattern to match
     * @return true if nodes were found, false if no nodes matched the pattern
     */
    public boolean findLabel(String expression){
        //marked = null;
        Pattern labelPattern = Pattern.compile(expression);
        Matcher matcher;
        // if selection is "", remove mark
        if (expression.compareTo("")==0){
            return false;
        }
        // Else, look for a match
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            
            matcher = labelPattern.matcher(node.getLabel());
            node.setFixed(false);
            if (matcher.find()){
                //marked = node;
                node.setFixed(true);
                //return node;
            }
        }
        return true;
    }
    
    
    /* Set a method of marking nodes that users search for
     * by displaying for instance an arrow
     */
    public void markNode(java.awt.Graphics2D g2d, Node markedNode){
        if (markedNode == null) {
            return;
        }
        // start by just drawing an arrow
        // from ref point 0,0
        int refX=(int) (getPanelWidth()/2);
        int refY=(int) (getPanelHeight()/2);
        double r45=Math.PI/7.0;
        double deltaLength=15.0;
        double headLength=13.0;
        
        // get theta
        double x = markedNode.getX();
        double y = markedNode.getY();
        double deltaX=(x-refX);
        double deltaY=-(y-refY);
        double theta=Math.atan(deltaY/deltaX);
        if (deltaX<0.0)
            theta+=Math.PI;
        
        // get x,y for the head point
        int xh=(int)(x-deltaLength*Math.cos(theta));
        int yh=(int)(y+deltaLength*Math.sin(theta));
        
        // get clockwise shoulder point
        int xc=(int)(xh-headLength*Math.cos(theta-r45));
        int yc=(int)(yh+headLength*Math.sin(theta-r45));
        
        // get anti-clockwise shoulder point
        int xa=(int)(xh-headLength*Math.cos(theta+r45));
        int ya=(int)(yh+headLength*Math.sin(theta+r45));
        
        // the shadow
        int shad=3;
        // build a polygon from this
        int[] xpoints={xc,xh,xa};
        int[] ypoints={yc,yh,ya};
        int[] shadowX={xc+shad,xh+shad,xa+shad};
        int[] shadowY={yc+shad,yh+shad,ya+shad};
        int npoints=3;
        
        // draw it
        //g2d.setColor(Color.magenta);
        g2d.setColor(Color.black);
        g2d.fillPolygon(shadowX, shadowY, npoints);
        g2d.setColor(Color.orange);
        g2d.fillPolygon(xpoints, ypoints, npoints);
        
        g2d.setColor(Color.black);
        
    }
    
    
    public int selectNodeByRegExp(final String regexp){
        int matches=0;
        Pattern labelPattern = Pattern.compile(regexp);
        Matcher matcher;
        // loop through and fix nodes that match the expression
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            matcher = labelPattern.matcher(node.getLabel());
            if (matcher.find()){
                node.setFixed(true);
                matches++;
            }
        }
        return matches;
    }
    
    /**
     * Reads a file of node names and selected those nodes in the graph.
     * @param fileName
     * @throws java.io.IOException
     * @return
     */
    public int selectNodeFromFile(final String fileName) throws IOException{
        int matches=0;
        String inLine;
        File loadFile = new File(fileName);
        BufferedReader in = new BufferedReader
                (new InputStreamReader(new FileInputStream(loadFile)));
        while((inLine=in.readLine())!=null){
            for(Iterator it = graph.nodesIterator(); it.hasNext();) {
                Node node = (Node)it.next();
                if (node.getLabel().compareTo(inLine)==0){
                    matches++;
                    node.setFixed(true);
                    continue;
                }
            }
        }
        in.close();
        return matches;
    }
    
    public void manipulateColor(int a, int b, boolean doSwitch){
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            if (node.isFixed()){
                node.manipulateColorElement(a,b,doSwitch);
            }
        }
    }
    
    public void addGradientX(int channel){
        int value;
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            if (node.isFixed()){
                value=(int)(node.getX()/getPanelWidth()*255.0);
                //System.out.println(node.getLabel()+" "+value);
                node.manipulateChannel(channel,value);
            }
        }
    }
    public void addGradientY(int channel){
        int value;
        for(Iterator it = graph.nodesIterator(); it.hasNext();) {
            Node node = (Node)it.next();
            if (node.isFixed()){
                value=(int)(node.getY()/getPanelWidth()*255.0);
                //System.out.println(node.getLabel()+" "+value);
                node.manipulateChannel(channel,value);
            }
        }
    }
    
    public void clearGraph(){
        graph.clear();
        updateNodes();
    }
    
    //Timer loadTimer;
    //public void loadGraphThread(){
    //	loadTimer = new Timer(200,loadGraphThreadActionEvent);
    
    
    
    
    
    DataLoader dl = new DataLoader(getPanelWidth(), getPanelHeight());
    public void loadGraph(String fileName) throws IOException, DataFormatException{
        graph = dl.load(fileName);
        updateNodes();
    }
    
    public void saveGraph(String fileName) throws IOException{
        dl.save(graph,fileName, scale);
        updateNodes();
    }
    
    public void writePajek(String fileName) throws IOException{
        dl.saveAsPajek(graph,fileName);
    }
    
    public void writePS(String fileName) throws IOException{
        dl.saveAsPS(graph,fileName,getStringSettings(), nodeSize, fontSize);
        //updateNodes();
    }
    public void writeEPS(String fileName) throws IOException{
        dl.saveAsEPS(graph,fileName,getStringSettings(),nodeSize, fontSize);
        //updateNodes();
    }
    /* Data manipulation tools
     */
    
    
    
    public void appendGraph(String fileName) throws IOException, DataFormatException{
        Graph temp = dl.load(fileName);
        graph.addGraph(temp);
        copyNodeSettings(temp);
        calculateEdgeLength();
        //System.out.println(graph.report());
        updateNodes();
    }
    
    public void addGraph(Graph g){
        if (g==null)
            return;
        g.rescaleNodes(600);
        graph.addGraph(g);
        copyNodeSettings(g);
    }
    
}



 

Ads help maintain this website.