package plab.prefuseTests;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Rectangle2D;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.JToggleButton;
import javax.swing.ButtonGroup;
import javax.swing.JRadioButton;
import javax.swing.JMenuItem;

import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.io.GraphMLReader;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphLib;
import prefuse.util.GraphicsLib;
import prefuse.util.display.DisplayLib;
import prefuse.util.display.ItemBoundsListener;
import prefuse.util.force.ForceSimulator;
import prefuse.util.io.IOLib;
import prefuse.util.ui.JForcePanel;
import prefuse.util.ui.JValueSlider;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;
import prefuse.action.assignment.StrokeAction;
import prefuse.util.StrokeLib;
import prefuse.action.assignment.DataSizeAction;
import prefuse.Constants;
import prefuse.data.Node;
import java.util.Iterator;
import prefuse.util.ui.UILib;
import prefuse.util.force.Force;
import prefuse.action.assignment.DataColorAction;
import prefuse.render.ShapeRenderer;
import prefuse.data.tuple.DefaultTupleSet;
import prefuse.render.EdgeRenderer;


/**
 * Primary test iteration for MSMGlasses.
 *
 * Built on GraphView framework by Jeffrey Heer.
 *
 * @author Bryce Cronkite-Ratcliff
 */
public final class GraphImageTest2backup extends JPanel {

    private static final int SIZE_THRESHOLD = 250;
    private static final String graph = "graph";
    private static final String nodes = "graph.nodes";
    private static final String edges = "graph.edges";

    private Visualization m_vis;

    /**
     * Constructor
     * 
     * @param g
     * @param label
     */
    public GraphImageTest2backup(final Graph g, String label) {
    	super(new BorderLayout());

        boolean isBigGraph = (g.getNodeCount() > SIZE_THRESHOLD);

        // create a new, empty visualization for our data
        m_vis = new Visualization();


        final LabelRenderer tr = new LabelRenderer();
        tr.setVerticalAlignment(Constants.BOTTOM);
        tr.setRoundedCorner(8, 8);
        m_vis.setRendererFactory(new DefaultRendererFactory(tr/*, new SelfRefEdgeRenderer()*/));
        ((EdgeRenderer)((DefaultRendererFactory)m_vis.getRendererFactory()).getDefaultEdgeRenderer()).setEdgeType(Constants.EDGE_TYPE_CURVE);

        // --------------------------------------------------------------------
        // register the data with a visualization

        // adds graph to visualization and sets renderer label field
        setGraph(g, label);

        boolean TPTopen = false;

        // fix selected focus nodes
        final TupleSet focusGroup = m_vis.getGroup(Visualization.FOCUS_ITEMS);
        focusGroup.addTupleSetListener(new TupleSetListener() {
            public void tupleSetChanged(TupleSet ts, Tuple[] add, Tuple[] rem)
            {
                for ( int i=0; i<rem.length; ++i ) {
                    ((VisualItem)rem[i]).setFixed(false);
                    Iterator focusEdges = ((Node)rem[i]).edges();
                    while (focusEdges.hasNext()) {
                        VisualItem currEdge = (VisualItem)focusEdges.next();
                        currEdge.setHover(false);
                    }
                }
                for ( int i=0; i<add.length; ++i ) {
                    ((VisualItem)add[i]).setFixed(false);
                    Iterator focusEdges = ((Node)add[i]).edges();
                    while (focusEdges.hasNext()) {
                        VisualItem currEdge = (VisualItem)focusEdges.next();
                        currEdge.setHover(false);
                    }
                    ((VisualItem)add[i]).setFixed(true);
                    Iterator focusEdges2 = ((Node)add[i]).edges();
                    while (focusEdges2.hasNext()) {
                        VisualItem currEdge = (VisualItem)focusEdges2.next();
                        currEdge.setHover(true);
                    }
                }
                if ( ts.getTupleCount() == 0 ) {
                    ts.addTuple(rem[0]);
                    ((VisualItem)rem[0]).setFixed(false);
                    Iterator focusEdges2 = ((Node)rem[0]).edges();
                    while (focusEdges2.hasNext()) {
                        VisualItem currEdge = (VisualItem)focusEdges2.next();
                        currEdge.setHover(false);
                    }
                }
                if (ts.getTupleCount() > 1)
                {
                    System.out.println("Got you! HAH!");
                    System.out.println(ts.toString());

                    Iterator tuples = ts.tuples();

                    String source;
                    String sink;

                    DefaultTupleSet A = new DefaultTupleSet();
                    A.addTuple((Tuple)tuples.next());

                    DefaultTupleSet B = new DefaultTupleSet();
                    B.addTuple((Tuple)tuples.next());

                   // JFrame tptFrame = new JFrame();
                    TPTWindow tptw = new TPTWindow(g, A, B);
                   // tptFrame.setContentPane(tptw);
                   // tptFrame.pack();
                   // tptFrame.setVisible(true);

                    focusGroup.clear();
                }
                
                m_vis.run("draw");
            }
        });

        initGraph(g, m_vis);
        if (g.getNodeCount() < SIZE_THRESHOLD)
            tr.getImageFactory().preloadImages(g.nodes(), "image");


        // --------------------------------------------------------------------
        // set up a display to show the visualization

        Display display = new Display(m_vis);
        display.setSize(700,700);
        display.pan(350, 350);
        display.setForeground(Color.GRAY);
        display.setBackground(Color.WHITE);

        // main display controls
        display.addControlListener(new FocusControlWithDeselect(1));
        display.addControlListener(new DragControl());
        display.addControlListener(new PanControl());
        display.addControlListener(new ZoomControl());
        display.addControlListener(new WheelZoomControl());
        display.addControlListener(new ZoomToFitControl());
        display.addControlListener(new NeighborHighlightControl());

        display.setForeground(Color.GRAY);
        display.setBackground(Color.WHITE);
        
        display.setHighQuality(true);


        JPanel fpanel = new JPanel();
        fpanel.setLayout(new BoxLayout(fpanel, BoxLayout.Y_AXIS));
        fpanel.setBackground(Color.WHITE);


        final JValueSlider slider = new JValueSlider("Distance", 0, 40, 37);
        slider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                ((GraphDistanceFilter)((ActionList)m_vis.getAction("draw")).get(0)).setDistance(slider.getValue().intValue());
                m_vis.run("draw");
            }
        });
        slider.setBackground(Color.WHITE);
        slider.setPreferredSize(new Dimension(300,30));
        slider.setMaximumSize(new Dimension(300,30));

        Box cf = new Box(BoxLayout.Y_AXIS);
        cf.add(slider);
        cf.setBorder(BorderFactory.createTitledBorder("Connectivity Filter"));
        fpanel.add(cf);

       final JValueSlider probSlider = new JValueSlider("Probability Threshold",
               0.0, 1.0, 1.0);
       probSlider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                Iterator allEdges = g.edges();
                double threshold = probSlider.getValue().doubleValue();
                while (allEdges.hasNext())
                {
                    VisualItem currEdge = (VisualItem)allEdges.next();
                    double prob = currEdge.getDouble("probability");
                    if (prob < threshold)
                    {
                        System.out.println("found");
                        currEdge.setVisible(false);
                    } else
                        currEdge.setVisible(true);
                }

                m_vis.run("draw");
            }
        });
        probSlider.setBackground(Color.WHITE);
        probSlider.setPreferredSize(new Dimension(300,30));
        probSlider.setMaximumSize(new Dimension(300,30));

        Box pf = new Box(BoxLayout.Y_AXIS);
        pf.add(probSlider);
        pf.setBorder(BorderFactory.createTitledBorder("Probability Filter"));
        fpanel.add(pf);



        JButton pause = new JButton("Stop Layout");
        pause.addActionListener( new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                //if (((ActionList)m_vis.getAction("lll")).isRunning())
                    ((ActionList)m_vis.getAction("lll")).cancel();
            }
        });
        if (g.getNodeCount() > SIZE_THRESHOLD) //HERE
            pause.setEnabled(false);

        JButton start = new JButton("Run Layout");
        start.addActionListener( new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                //if (!lll.isScheduled())
                    ((ActionList)m_vis.getAction("lll")).run();
            }
        });
        if (g.getNodeCount() > SIZE_THRESHOLD) //HERE
            start.setEnabled(false);

        Box runControls = new Box(BoxLayout.X_AXIS);
        runControls.add(start);
        runControls.add(pause);
        runControls.setBorder(
                BorderFactory.createTitledBorder("Run Control"));
        fpanel.add(runControls);

        JButton openImg = new JButton("Open Image");
        openImg.addActionListener( new ActionListener() {
           public void actionPerformed( ActionEvent ae ) {
               Tuple focus = (Tuple)m_vis.getGroup(Visualization.FOCUS_ITEMS).tuples().next();
               Picture imgFrame = new Picture((String)focus.get("image"));
               imgFrame.show();

               
           }
        });
        fpanel.add(openImg);

        JButton exportDisplay = new JButton("Save Image");
        exportDisplay.addActionListener( new EnhancedExportDisplayAction(display) );
        fpanel.add(exportDisplay);

        JToggleButton togglePics = new JToggleButton("Show Images", false);
        togglePics.addActionListener( new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    JToggleButton tp = (JToggleButton) ae.getSource();
                    if(!tp.isSelected())
                    {
                        tr.setImageField(null);
                        ((DataSizeAction)m_vis.getAction("nodeSize")).setMaximumSize(50.0);
                        m_vis.run("nodeSize");
                       // draw.add(nodeSize);
                    } else
                    {
                        //m_vis.removeAction("nodeSize");
                        ((DataSizeAction)m_vis.getAction("nodeSize")).setMaximumSize(1.0);
                        m_vis.run("nodeSize");
                        tr.setImageField("image");
                        tr.setImagePosition(Constants.TOP);
                        tr.getImageFactory().setMaxImageDimensions(100, 100);
                        
                        
                    }
                    m_vis.run("draw");
                }
        }); 

        JToggleButton togglePM = new JToggleButton("Picture Mode", false);
        togglePM.addActionListener( new ActionListener() {
            public void actionPerformed( ActionEvent ae ) {
                JToggleButton tpm = (JToggleButton)ae.getSource();
                if (tpm.isSelected()) {
                } else {       
                }
            }
        });

        Box renderControls = new Box(BoxLayout.X_AXIS);
        renderControls.setBorder(BorderFactory.createTitledBorder("Render Control"));
        renderControls.add(togglePics);
        renderControls.add(togglePM);
        fpanel.add(renderControls);

        ButtonGroup nodeRenderers = new ButtonGroup();

        JRadioButton circleRB = new JRadioButton("Circle", false);
        circleRB.addActionListener( new ActionListener() {
            public void actionPerformed( ActionEvent ae ) {
                JRadioButton circleRB = (JRadioButton)ae.getSource();
                if (circleRB.isSelected()) {
                    m_vis.setRendererFactory(new DefaultRendererFactory(new ShapeRenderer()/*, new SelfRefEdgeRenderer()*/));
                    m_vis.setValue(nodes, null, VisualItem.SHAPE, Constants.SHAPE_ELLIPSE);
                }

            }
        });
        nodeRenderers.add(circleRB);

        JRadioButton labelRB = new JRadioButton("Label", false);
        labelRB.addActionListener( new ActionListener() {
            public void actionPerformed( ActionEvent ae ) {
                JRadioButton labelRB = (JRadioButton)ae.getSource();
                if (labelRB.isSelected()) {
                    m_vis.setRendererFactory(new DefaultRendererFactory(tr/*, new SelfRefEdgeRenderer()*/));
                }

            }
        });
        nodeRenderers.add(labelRB);

        Box nrBox = new Box(BoxLayout.X_AXIS);
        nrBox.setBorder(BorderFactory.createTitledBorder("Node Renderer"));
        nrBox.add(circleRB);
        nrBox.add(labelRB);
        labelRB.setSelected(true);

        fpanel.add(nrBox);
        
        Display overview = new Display(m_vis);
        overview.setSize(290,290);
        overview.addItemBoundsListener(new FitOverviewListener());
        overview.setHighQuality(true);
        


        JPanel opanel = new JPanel();
        opanel.setBorder(BorderFactory.createTitledBorder("Overview"));
        opanel.setBackground(Color.WHITE);
        opanel.add(overview);

        fpanel.add(opanel);

        fpanel.add(Box.createVerticalGlue());



        // create a new JSplitPane to present the interface
        JSplitPane split = new JSplitPane();
        split.setLeftComponent(display);
        split.setRightComponent(fpanel);
        split.setOneTouchExpandable(true);
        split.setContinuousLayout(false);
        split.setDividerLocation(700);

        // now we run our action list
        m_vis.run("draw");
        m_vis.run("lll"); //HERE
        m_vis.run("nodeSize");

        add(split);
    }

    public void setGraph(Graph g, String label) {
        // update labeling
        DefaultRendererFactory drf = (DefaultRendererFactory)
                                                m_vis.getRendererFactory();
        //((LabelRenderer)drf.getDefaultRenderer()).setTextField(label);

        // update graph
        m_vis.removeGroup(graph);
        VisualGraph vg = m_vis.addGraph(graph, g);
        m_vis.setValue(edges, null, VisualItem.INTERACTIVE, Boolean.FALSE);
        VisualItem f = (VisualItem)vg.getNode(0);
        m_vis.getGroup(Visualization.FOCUS_ITEMS).setTuple(f);
        f.setFixed(false);
    }

    public void initGraph(Graph g, Visualization vis) {

        ColorAction fill = new ColorAction(nodes,
                VisualItem.FILLCOLOR, ColorLib.rgb(179, 255, 156));
        fill.add(VisualItem.FIXED, ColorLib.rgb(255,100,100));
        fill.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255,200,125));

        DataColorAction edgeColor = new DataColorAction(edges, "probability",
                  Constants.NOMINAL, VisualItem.STROKECOLOR,
                  ColorLib.getGrayscalePalette());
        edgeColor.add(VisualItem.HOVER, ColorLib.rgb(0,0,200));
        edgeColor.add(VisualItem.HIGHLIGHT , ColorLib.rgb(255,150,68));

        DataColorAction edgeFill = new DataColorAction(edges, "probability",
                Constants.NOMINAL, VisualItem.FILLCOLOR,
                //ColorLib.getInterpolatedPalette(ColorLib.rgba(30, 30, 30, 0), ColorLib.rgba(30,30,30,225)));
                ColorLib.getGrayscalePalette());
        edgeFill.add(VisualItem.HIGHLIGHT , ColorLib.rgb(200,0,0));

        StrokeAction edgeWeight = new StrokeAction(edges,
                StrokeLib.getStroke(1.0f));
        edgeWeight.add(VisualItem.HIGHLIGHT, StrokeLib.getStroke(2.0f));

        StrokeAction nodeWeight = new StrokeAction(nodes,
                StrokeLib.getStroke(1.0f));
        edgeWeight.add(VisualItem.HIGHLIGHT, StrokeLib.getStroke(2.0f));

        DataSizeAction nodeSize = new DataSizeAction(nodes,
                "eqProb", 50, Constants.LOG_SCALE);
        nodeSize.setMaximumSize(50.0);

        g.getNodes().addColumn("image", "CONCAT('/images/macro',label,'.png')");

        int hops = 5;
        final GraphDistanceFilter filter = new GraphDistanceFilter(graph, 40);


        final ActionList draw = new ActionList();
        draw.add(filter);
        //draw.add(edgeWeight);
        draw.add(edgeFill);
        draw.add(new ColorAction(nodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0)));
        draw.add(new ColorAction(nodes, VisualItem.STROKECOLOR, ColorLib.gray(50)));

        ActionList animate = new ActionList(ActionList.INFINITY);
        animate.add(edgeWeight);
        animate.add(edgeColor);
        animate.add(fill);
        animate.add(new RepaintAction());

        final ActionList lll = new ActionList();
        if (g.getNodeCount() > SIZE_THRESHOLD)                                //If graph is large
            lll.add(new ForceDirectedLayout(graph, false, true)); //Then run-once
         else {
            lll.setDuration(ActionList.INFINITY);
            lll.add(new ForceDirectedLayout(graph));              //Else, continually animate
        }




        // finally, we register our ActionList with the Visualization.
        // we can later execute our Actions by invoking a method on our
        // Visualization, using the name we've chosen below.
        m_vis.putAction("draw", draw);
        m_vis.putAction("lll",lll);
        m_vis.putAction("nodeSize", nodeSize);
        m_vis.putAction("layout", animate);

        m_vis.runAfter("draw", "layout");

        ForceSimulator fsim = ((ForceDirectedLayout)((ActionList)m_vis.getAction("lll")).get(0)).getForceSimulator(); //HERE
        Force[] forces = fsim.getForces();

        final Force NBodyForce = forces[0];
        //final Force DragForce = forces[1];
        final Force SpringForce = forces[2];

        SetForces: {
            NBodyForce.setMinValue(0, -40.0f);
            NBodyForce.setParameter(0, -40.0f);
            NBodyForce.setParameter(1, -1);

            SpringForce.setParameter(0, .000001f);
            SpringForce.setParameter(1, 200f);
        }

    }

    // ------------------------------------------------------------------------
    // Main and demo methods
    // ------------------------------------------------------------------------

    public static void main(String[] args) {
        UILib.setPlatformLookAndFeel();

        // create graphview
        String datafile = null;
        String label = "label";
        if ( args.length > 1 ) {
            datafile = args[0];
            label = args[1];
        }

        JFrame frame = demo("5macro.xml", label); //HERE
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }   // end of main

    public static JFrame demo() {
        return demo((String)null, "label");
    }   // end of class demo()

    public static JFrame demo(String datafile, String label) {
        Graph g = null;
        if ( datafile == null ) {
            g = GraphLib.getGrid(15,15);
            label = "label";
        } else {
            try {
                g = new GraphMLReader().readGraph(datafile);
            } catch ( Exception e ) {
                System.err.println(e.getMessage());
                System.err.println(e.getCause());
                System.err.println("fuck. =[");
                System.exit(1);
            }
        }
        return demo(g, label);
    } // end of class demo(String, String)

    public static JFrame demo(final Graph g, String label) {
        final GraphImageTest2backup view = new GraphImageTest2backup(g, label);

        JMenuItem openAdj = new JMenuItem("Adjustment Panel");
        openAdj.addActionListener( new ActionListener() {
           public void actionPerformed( ActionEvent ae ) {
                JFrame forceFrame = new JFrame("Force Adjustment");

                ForceSimulator fsim = ((ForceDirectedLayout)((ActionList)view.m_vis.getAction("lll")).get(0)).getForceSimulator();
                JForcePanel fPanel = new JForcePanel(fsim);
                forceFrame.add(fPanel);
                forceFrame.pack();
                forceFrame.setVisible(true);
           }
        });

        JMenuItem makeMovie = new JMenuItem("Create PDB Movie");
        makeMovie.addActionListener( new ActionListener() {

            public void actionPerformed( ActionEvent ae ) {
                PDBFrame pdbf = new PDBFrame(g, view.m_vis);
            }

        });
        
        
        // set up menu
        JMenu dataMenu = new JMenu("Data");
        dataMenu.add(openAdj);
        dataMenu.add(makeMovie);
        dataMenu.add(new OpenGraphAction(view));
        
        JMenuBar menubar = new JMenuBar();
        menubar.add(dataMenu);

        // launch window
        JFrame frame = new JFrame("p r e f u s e  |  g r a p h v i e w");
        frame.setJMenuBar(menubar);
        frame.setContentPane(view);
        frame.pack();
        frame.setVisible(true);

        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowActivated(WindowEvent e) {
                view.m_vis.run("layout");
            }
            @Override
            public void windowDeactivated(WindowEvent e) {
                if ((((JFrame)e.getWindow()).getTitle()).equals("Force Adjustment")) {
                    view.m_vis.cancel("layout");
                }
            }
        });

        return frame;
    }   //end of class demo(Graph, String)

    // ------------------------------------------------------------------------
    //  Utility classes
    // ------------------------------------------------------------------------

    /**
     * Swing menu action that loads a graph into the graph viewer.
     *
     * I'm pretty sure this is useless. TODO: deal with it.
     */
    public abstract static class GraphMenuAction extends AbstractAction {
        private GraphImageTest2backup m_view;
        public GraphMenuAction(String name, String accel, GraphImageTest2backup view) {
            m_view = view;
            this.putValue(AbstractAction.NAME, name);
            this.putValue(AbstractAction.ACCELERATOR_KEY,
                          KeyStroke.getKeyStroke(accel));
        }
        public void actionPerformed(ActionEvent e) {
            Graph g = getGraph();
            m_view.setGraph(g, "label");
            m_view.initGraph(g, m_view.m_vis);
            
            m_view.m_vis.run("draw");
            m_view.m_vis.run("lll");
            m_view.m_vis.run("layout");
            m_view.m_vis.run("nodeSize");
            
        }
        protected abstract Graph getGraph();
    }   //end of class GraphMenuAction


    /**
     * Really awfully messy method to open a new graph from
     * the menu "Open Graph" option. TODO: fix this shit.
     *
     */
    public static class OpenGraphAction extends AbstractAction {
        private GraphImageTest2backup m_view;

        public OpenGraphAction(GraphImageTest2backup view) {
            m_view = view;
            this.putValue(AbstractAction.NAME, "Open File...");
            this.putValue(AbstractAction.ACCELERATOR_KEY,
                          KeyStroke.getKeyStroke("ctrl O"));
        }
        public void actionPerformed(ActionEvent e) {
            
            Graph g = IOLib.getGraphFile(m_view);

            m_view.setGraph(g, "label");
            m_view.initGraph(g, m_view.m_vis);

            m_view.m_vis.run("draw");
            m_view.m_vis.run("lll");
            m_view.m_vis.run("layout");
            m_view.m_vis.run("nodeSize");
        }
        public static String getLabel(Component c, Graph g) {
            // get the column names
            Table t = g.getNodeTable();
            int  cc = t.getColumnCount();
            String[] names = new String[cc];
            for ( int i=0; i<cc; ++i )
                names[i] = t.getColumnName(i);

            // where to store the result
            final String[] label = new String[1];

            // -- build the dialog -----
            // we need to get the enclosing frame first
            while ( c != null && !(c instanceof JFrame) ) {
                c = c.getParent();
            }
            final JDialog dialog = new JDialog(
                    (JFrame)c, "Choose Label Field", true);

            // create the ok/cancel buttons
            final JButton ok = new JButton("OK");
            ok.setEnabled(false);
            ok.addActionListener(new ActionListener() {
               public void actionPerformed(ActionEvent e) {
                   dialog.setVisible(false);
               }
            });
            JButton cancel = new JButton("Cancel");
            cancel.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    label[0] = null;
                    dialog.setVisible(false);
                }
            });

            // build the selection list
            final JList list = new JList(names);
            list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            list.getSelectionModel().addListSelectionListener(
            new ListSelectionListener() {
                public void valueChanged(ListSelectionEvent e) {
                    int sel = list.getSelectedIndex();
                    if ( sel >= 0 ) {
                        ok.setEnabled(true);
                        label[0] = (String)list.getModel().getElementAt(sel);
                    } else {
                        ok.setEnabled(false);
                        label[0] = null;
                    }
                }
            });
            JScrollPane scrollList = new JScrollPane(list);

            JLabel title = new JLabel("Choose a field to use for node labels:");

            // layout the buttons
            Box bbox = new Box(BoxLayout.X_AXIS);
            bbox.add(Box.createHorizontalStrut(5));
            bbox.add(Box.createHorizontalGlue());
            bbox.add(ok);
            bbox.add(Box.createHorizontalStrut(5));
            bbox.add(cancel);
            bbox.add(Box.createHorizontalStrut(5));

            // put everything into a panel
            JPanel panel = new JPanel(new BorderLayout());
            panel.add(title, BorderLayout.NORTH);
            panel.add(scrollList, BorderLayout.CENTER);
            panel.add(bbox, BorderLayout.SOUTH);
            panel.setBorder(BorderFactory.createEmptyBorder(5,2,2,2));

            // show the dialog
            dialog.setContentPane(panel);
            dialog.pack();
            dialog.setLocationRelativeTo(c);
            dialog.setVisible(true);
            dialog.dispose();

            // return the label field selection
            return label[0];
        }
    } // end of class OpenGraphAction


    /**
     * Adjusts scale of overview window to fit graph
     * to viewing window.
     */
    public static class FitOverviewListener implements ItemBoundsListener {
        private Rectangle2D m_bounds = new Rectangle2D.Double();
        private Rectangle2D m_temp = new Rectangle2D.Double();
        private double m_d = 15;
        public void itemBoundsChanged(Display d) {
            d.getItemBounds(m_temp);
            GraphicsLib.expand(m_temp, 25/d.getScale());

            double dd = m_d/d.getScale();
            double xd = Math.abs(m_temp.getMinX()-m_bounds.getMinX());
            double yd = Math.abs(m_temp.getMinY()-m_bounds.getMinY());
            double wd = Math.abs(m_temp.getWidth()-m_bounds.getWidth());
            double hd = Math.abs(m_temp.getHeight()-m_bounds.getHeight());
            if ( xd>dd || yd>dd || wd>dd || hd>dd ) {
                m_bounds.setFrame(m_temp);
                DisplayLib.fitViewToBounds(d, m_bounds, 0);
            }
        }
    } // end of class FitOverviewListener
} // end of class GraphView
