001package net.kreatious.pianoleopard.painter;
002
003import java.awt.Dimension;
004import java.awt.Graphics;
005import java.awt.Graphics2D;
006import java.awt.event.ComponentAdapter;
007import java.awt.event.ComponentEvent;
008
009import javax.swing.JPanel;
010
011import net.kreatious.pianoleopard.midi.InputModel;
012import net.kreatious.pianoleopard.midi.OutputModel;
013import net.kreatious.pianoleopard.midi.track.ParsedSequence;
014import net.kreatious.pianoleopard.midi.track.ParsedTrack;
015
016/**
017 * Renders the currently playing sequence into a panel using double buffering.
018 *
019 * @author Jay-R Studer
020 */
021public class PainterPanel {
022    private final class PainterPanelImpl extends JPanel {
023        private static final long serialVersionUID = 3647951920113354307L;
024        private final Painter painter = new Painter(new Dimension());
025
026        PainterPanelImpl() {
027            setPreferredSize(new Dimension(1000, 500));
028            addComponentListener(new ComponentAdapter() {
029                @Override
030                public void componentResized(ComponentEvent e) {
031                    painter.setComponentDimensions(getSize());
032                }
033            });
034        }
035
036        @Override
037        public void paint(Graphics g) {
038            painter.paint((Graphics2D) g, currentTime, sequence, playedTrack);
039        }
040    }
041
042    private final JPanel panel = new PainterPanelImpl();
043    private final ParsedTrack playedTrack;
044
045    private volatile long currentTime;
046    private volatile ParsedSequence sequence = ParsedSequence.createEmpty();
047
048    /**
049     * Constructor declared private to prevent direct instantiation by
050     * consumers.
051     */
052    private PainterPanel(ParsedTrack playedTrack) {
053        this.playedTrack = playedTrack;
054    }
055
056    /**
057     * Constructs a new {@link PainterPanel} connected to the specified track
058     * containing events played by the user
059     *
060     * @param outputModel
061     *            the output model for events sent to the synthesizer
062     * @param inputModel
063     *            the input model for events played by the user
064     * @return a new instance of {@link PainterPanel}
065     */
066    public static JPanel create(OutputModel outputModel, InputModel inputModel) {
067        final PainterPanel result = new PainterPanel(inputModel);
068        outputModel.addCurrentTimeListener(result::setCurrentTime);
069        outputModel.addOpenListener(result::setCurrentSequence);
070        return result.getPanel();
071    }
072
073    /**
074     * Gets the panel associated with this component
075     *
076     * @return the panel owned by this component
077     */
078    private JPanel getPanel() {
079        return panel;
080    }
081
082    /**
083     * @param currentTime
084     *            the current song time in microseconds
085     */
086    private void setCurrentTime(long currentTime) {
087        this.currentTime = currentTime;
088        panel.repaint();
089    }
090
091    /**
092     * Sets the current sequence displayed by this panel
093     *
094     * @param sequence
095     *            the parsed sequence to set
096     */
097    private void setCurrentSequence(ParsedSequence sequence) {
098        this.sequence = sequence;
099    }
100}