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}