package p2pp; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.border.Border; import org.klomp.snark.Peer; import org.klomp.snark.PeerCoordinator; public class GUIDownloadProgressListener extends JComponent implements ComponentListener, DownloadProgressListener { private JLabel text; private JLabel progress; private Timer timer; private double nbOfPieces; private int piecesDownloaded = 0; private PeerCoordinator coord; private Map<Integer, Color> pieces = new HashMap<Integer, Color>(); private double barWidth; private double playbackPosition = -1; private final int BAR_HEIGHT = 30; private final int INIT_BAR_WIDTH = 1024; private final int X = 10; private final int Y = 10; public GUIDownloadProgressListener(int nbOfPieces, final double bitrate, final int wait) { this(nbOfPieces); timer = new Timer(100, new ActionListener() { private long start = 0; @Override public void actionPerformed(ActionEvent event) { if(start == 0) start = System.currentTimeMillis(); double speed = bitrate * ((System.currentTimeMillis() - start) / 1000.0); playbackPosition = speed * barWidth / GUIDownloadProgressListener.this.nbOfPieces; //System.out.println("playback=" + playbackPosition); if(playbackPosition >= barWidth) { playbackPosition = barWidth; timer.stop(); } EventQueue.invokeLater(new Runnable() { @Override public void run() { GUIDownloadProgressListener.this.repaint(); } }); } }); timer.setInitialDelay(wait * 1000); timer.start(); } public GUIDownloadProgressListener(int nbOfPieces) { this.nbOfPieces = nbOfPieces; final JFrame frame = new JFrame("Piece Download Progress"); frame.getContentPane().add(this, BorderLayout.CENTER); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.addComponentListener(this); text = new JLabel("Initialized"); Border paddingBorder = BorderFactory.createEmptyBorder(0,10,5,0); text.setBorder(paddingBorder); frame.getContentPane().add(text, BorderLayout.SOUTH); progress = new JLabel("0%"); progress.setPreferredSize(new Dimension(50, 50)); progress.setBorder(paddingBorder); frame.getContentPane().add(progress, BorderLayout.WEST); JPanel legends = new JPanel(new FlowLayout()); legends.add(new Legend(Color.BLUE, "Got piece from peer"), BorderLayout.NORTH); legends.add(new Legend(Color.GREEN, "Got piece from server"), BorderLayout.NORTH); legends.add(new Legend(Color.RED, "Piece requested"), BorderLayout.NORTH); frame.getContentPane().add(legends, BorderLayout.NORTH); this.barWidth = INIT_BAR_WIDTH; this.setPreferredSize(new Dimension(INIT_BAR_WIDTH + 2 * X, BAR_HEIGHT + 2 * Y)); EventQueue.invokeLater(new Runnable() { @Override public void run() { frame.pack(); frame.setVisible(true); } }); } @Override public void pieceRequested(Peer peer, int piece) { synchronized(pieces) { if(pieces.containsKey(piece)) return; pieces.put(piece, Color.RED); EventQueue.invokeLater(new Runnable() { @Override public void run() { GUIDownloadProgressListener.this.repaint(); } }); } } @Override public void downloadComplete() { coord.halt(); synchronized(text) { EventQueue.invokeLater(new Runnable() { @Override public void run() { text.setText("Download complete!"); } }); } } @Override public void setPeerCoordinator(PeerCoordinator coord) { this.coord = coord; } @Override public void pieceDownloaded(final Peer peer, final int piece) { synchronized(pieces) { Color color = peer == null ? Color.GREEN : Color.BLUE; piecesDownloaded++; pieces.put(piece, color); EventQueue.invokeLater(new Runnable() { @Override public void run() { String from = peer == null ? WebSeed.description : peer.toString(); text.setText("Recieved piece " + piece + " from " + from + "!"); double prog = ((double) piecesDownloaded) / nbOfPieces; progress.setText(((int) (prog * 100)) + "%"); GUIDownloadProgressListener.this.repaint(); } }); } } @Override public void paintComponent(Graphics g) { //g.drawImage(background, -100, -320, null); super.paintComponents(g); } @Override public void paint(Graphics g) { synchronized(pieces) { super.paint(g); Graphics2D g2 = (Graphics2D) g; Rectangle2D.Double bar = new Rectangle2D.Double(X, Y, barWidth, BAR_HEIGHT); g2.setColor(Color.WHITE); g2.fill(bar); if(!pieces.isEmpty()) { double pieceWidth = barWidth / nbOfPieces; ArrayList<Integer> keys = new ArrayList<Integer>(pieces.keySet()); Collections.sort(keys); int first = keys.get(0); int currentPiece = first; Color currentColor = pieces.get(first); int currentPieceCount = 0; for(int piece : keys) { Color color = pieces.get(piece); if(color.equals(currentColor) && currentPiece++ == piece) currentPieceCount++; else { drawPieces(g2, pieceWidth, currentColor, first, currentPiece, currentPieceCount); currentColor = pieces.get(piece); currentPieceCount = 1; first = piece; currentPiece = piece + 1; } } drawPieces(g2, pieceWidth, currentColor, first, currentPiece, currentPieceCount); } Line2D.Double play = new Line2D.Double(X + playbackPosition, Y, X + playbackPosition, Y + BAR_HEIGHT); g2.setColor(Color.BLACK); g2.draw(play); g2.draw(bar); } } @Override public void componentResized(ComponentEvent e) { Component frame = e.getComponent(); barWidth = ((JFrame) frame).getContentPane().getWidth() - 2 * X - progress.getWidth(); } @Override public void componentMoved(ComponentEvent e) {} @Override public void componentShown(ComponentEvent e) {} @Override public void componentHidden(ComponentEvent e) {} private void drawPieces(Graphics2D g2, double pieceWidth, Color currentColor, int first, int currentPiece, int currentPieceCount) { double x = X + first * pieceWidth; double width = pieceWidth * currentPieceCount; Rectangle2D.Double mark = new Rectangle2D.Double(x, Y, width, BAR_HEIGHT); g2.setColor(currentColor); g2.fill(mark); g2.setColor(new Color(0,0,0,0)); g2.draw(mark); } static class Legend extends JLabel { public Legend(Color color, String text) { //this.color = color; //this.text = text; Border paddingBorder = BorderFactory.createEmptyBorder(0,10,0,10); this.setBorder(paddingBorder); this.setIcon(makeIcon(color, 10)); this.setText(text); } private Icon makeIcon(final Color color, final int r) { return new Icon() { @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2 = (Graphics2D) g; Ellipse2D.Double circle = new Ellipse2D.Double( x, y, r, r); g2.setColor(color); g2.fill(circle); g2.draw(circle); } @Override public int getIconWidth() { return r; } @Override public int getIconHeight() { return r; } }; } } }