/** * Jeff Rupp * Master's Thesis * 2005 * * This class implements a generic data graphing panel * */ package jdr.utils; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; public class DataGraph extends JPanel implements ComponentListener, MouseListener { private WaitToRedrawThread m_resizeThread; private Object m_threadLock = new Object(); private DrawThread m_drawThread = null; // Members for doing double-buffering. private boolean m_safeToCreateImage = false; private java.awt.image.BufferedImage m_offScreen = null; private Graphics2D m_offScreenGraphics = null; private Vector m_linesVector = new Vector(); private String m_startXLabel = ""; private String m_stopXLabel = ""; private String m_centerXLabel = ""; // area to the right to display labels, no grid public static final int LABEL_WIDTH = 80; public static final int LABEL_HEIGHT = 40; private Color m_backGroundColor = Color.black; private Color m_gridColor = new Color(0, 0xaa, 0); private java.text.DecimalFormat m_dblFmt = new java.text.DecimalFormat("0.0000E00"); private JMenu m_popupMenu = new JMenu(); private JMenuItem m_propertiesMenuItem = new JMenuItem("Properties..."); private boolean m_drawBlackAndWhite = false; public DataGraph() { super(); addComponentListener(this); addMouseListener(this); m_popupMenu.add(m_propertiesMenuItem); m_propertiesMenuItem.addActionListener(new ActionListener() { public void actionPerformed(java.awt.event.ActionEvent A) { ShowPropertiesDialog(); } }); } /** * method used to draw a line on the graph, and associate a label with that line color */ public void AddLine(LineInfo lif) { synchronized(m_linesVector) { m_linesVector.addElement(lif); } } public void paint(Graphics g) { // have to do some double buffered action to first draw to buffer, // then transfer that buffer to the screen if(m_offScreenGraphics != null) { synchronized(m_offScreenGraphics) { ((Graphics2D)g).drawImage(m_offScreen, 0, 0, this); } } m_safeToCreateImage = true; } private void ClearScreen() { m_offScreenGraphics.setColor(m_backGroundColor); Rectangle currentBounds = getBounds(); // clear by drawing a filled rect in the current bounds m_offScreenGraphics.fillRect(0,0, currentBounds.width, currentBounds.height); } private void DrawGrid() { m_offScreenGraphics.setColor(m_gridColor); Rectangle currentBounds = getBounds(); int width = currentBounds.width - LABEL_WIDTH; int height = currentBounds.height - LABEL_HEIGHT; // draw vertical lines int inc = width / 10; for(int i = 0; i < width; i += inc) { m_offScreenGraphics.drawLine(i, 0, i, height); } // draw horizontal lines inc = height/10; for(int i = 0; i < height; i += inc) { m_offScreenGraphics.drawLine(0, i, width, i); } } public void componentResized(java.awt.event.ComponentEvent e) { // start a thread that waits for a second after the resizing // has finished before re-doing the drawing synchronized(m_threadLock) { if(m_resizeThread == null) { m_resizeThread = new WaitToRedrawThread(); m_resizeThread.start(); } else { m_resizeThread.setStillResizing(true); } } } public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } public void FinishedWithResizeThread() { synchronized(m_threadLock) { m_resizeThread = null; } } /** * redraw from the given vector of LineInfo s */ public void reDraw() { reDraw(m_linesVector); } public void reDraw(Vector linesVect) { synchronized(m_linesVector) { if(m_linesVector != linesVect) { m_linesVector = linesVect; } } if(m_drawThread == null) { m_drawThread = new DrawThread(); m_drawThread.start(); m_drawThread.setDrawRequested(true); } else { m_drawThread.setDrawRequested(true); } } public void setStartXLabel(String label) { m_startXLabel = label; } public void setStopXLabel(String label) { m_stopXLabel = label; } public void setCenterXLabel(String label) { m_centerXLabel = label; } // mouse event handlers /** * Invoked when the mouse has been clicked on a component. */ public void mouseClicked(MouseEvent e) { //System.out.println("mouseClicked"); ToggleBlackAndWhite(); } /** * Invoked when the mouse exits a component. */ public void mouseExited(MouseEvent e) { //System.out.println("mouseExited"); } /** * Invoked when a mouse button has been pressed on a component. */ public void mousePressed(MouseEvent e) { System.out.println("mousePressed, button: "+ e.getButton() + " isPopupTrigger: "+e.isPopupTrigger()); // also button 3 as my mouse is 3, but not a popup trigger if(e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3) { System.out.println("show popup menu at x,y: "+ e.getX()+", "+ e.getY()); m_popupMenu.setMenuLocation(e.getX(), e.getY()); m_popupMenu.setVisible(true); m_popupMenu.repaint(); } } /** * Invoked when a mouse button has been released on a component. */ public void mouseReleased(MouseEvent e) { //System.out.println("mouseReleased"); } /** * Invoked when the mouse enters a component. */ public void mouseEntered(MouseEvent e) { //System.out.println("mouseEntered"); } public void ShowPropertiesDialog() { System.out.println("ShowPropertiesDialog"); ToggleBlackAndWhite(); } /** * toggles the graphing mode between black/white and color */ public void ToggleBlackAndWhite() { System.out.println("ToggleBlackAndWhite"); m_drawBlackAndWhite = !m_drawBlackAndWhite; synchronized(m_linesVector) { for(int i = 0; i < m_linesVector.size(); ++i) { ((LineInfo)m_linesVector.get (i)).SetModeToBlackWhite(m_drawBlackAndWhite); } } if(m_drawBlackAndWhite) { m_backGroundColor = Color.white; m_gridColor = Color.black; } else { m_backGroundColor = Color.black; m_gridColor = new Color(0, 0xaa, 0); } reDraw(); } protected class WaitToRedrawThread extends Thread { public boolean m_stillResizing = true; public Object m_threadLock = new Object(); public WaitToRedrawThread() { super("GraphWaitToRedraw"); } public void run() { while(true) { synchronized(m_threadLock) { if(m_stillResizing) { m_stillResizing = false; } else { break; } } try { sleep(200); } catch(InterruptedException ignore) { } } // invoke the re-draw reDraw(m_linesVector); FinishedWithResizeThread(); } public void setStillResizing(boolean isResizing) { synchronized(m_threadLock) { m_stillResizing = isResizing; } } } protected class DrawThread extends Thread { private boolean m_drawRequested = false; private boolean m_okToKeepRunning = true; public DrawThread() { super("GraphDraw"); } public void setDrawRequested(boolean wantDraw) { m_drawRequested = wantDraw; } public boolean getDrawRequested() { return m_drawRequested; } public void run() { int width = 0; int height = 0; while(m_okToKeepRunning) { try { if(!m_drawRequested) { try { sleep(200); continue; } catch(InterruptedException ignore) { continue; } } Rectangle currentBounds = getBounds(); if((currentBounds.width <= 0) || (currentBounds.height <= 0)) { continue; } // only create a new double buffer when the size changes if((currentBounds.width != width) || (currentBounds.height != height)) { width = currentBounds.width; height = currentBounds.height; // have to do some double buffered action to first draw to buffer, // then transfer that buffer to the screen m_offScreen = (java.awt.image.BufferedImage)createImage(currentBounds.width, currentBounds.height); if(m_offScreen == null) { continue; } if(m_offScreenGraphics != null) { m_offScreenGraphics.dispose(); } m_offScreenGraphics = m_offScreen.createGraphics(); m_offScreenGraphics.setFont(new Font("Times New Roman", Font.PLAIN, 10)); } if(m_offScreen == null) { continue; } // If we're really smart we'll see about leaving the image alone, and // just adding to the lines already there // initially just re-draw the whole image each time ClearScreen(); DrawGrid(); // have to get all the data, then scale to fit the graph // will cache a scale factor and see if we exceed, if so then // double the scale factor (1/2 the graph detail) and re-commence // the drawing. // get the snapshot history from the Statistics class // or from the reDraw. Rectangle trimmedBounds = new Rectangle(); trimmedBounds.width = currentBounds.width - LABEL_WIDTH; trimmedBounds.height = currentBounds.height - LABEL_HEIGHT; synchronized(m_linesVector) { for(int i = 0; i < m_linesVector.size(); ++i) { ((LineInfo)m_linesVector.get (i)).drawLine(m_offScreenGraphics, currentBounds, trimmedBounds); } } repaint(); m_drawRequested = false; } catch(Exception ex) { ex.printStackTrace(); } } } } }