import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultListModel; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class TimeLineJPanel extends JPanel implements MouseWheelListener, FocusListener, ActionListener, ChangeListener, AdjustmentListener { private static final long serialVersionUID = 1L; private static final Font titleFont = new Font(new JLabel().getFont().getName(), Font.BOLD, 14); private static final int SCROLL_SPEED_HORIZONTAL = 30; private static final int SCROLL_SPEED_VERTICAL = 30; private DefaultListModel listModel; private double zoomLevel = 1; private JScrollPane scrollPane; private MusicRenderer musicRenderer; private Interface interf; private JTextField currentTick; private JLabel maxTicks; private JPanel scrollPanel; public TimeLineJPanel(Interface interf) { this.interf = interf; setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); // timing infos JPanel timingPanel = new JPanel(); timingPanel.setLayout(new BoxLayout(timingPanel, BoxLayout.LINE_AXIS)); JLabel title = new JLabel("Timeline : "); title.setFont(titleFont); timingPanel.add(title); currentTick = new JTextField("00:00.000", 8); currentTick.setMaximumSize( currentTick.getPreferredSize() ); currentTick.addFocusListener(this); currentTick.addActionListener(this); timingPanel.add(currentTick); maxTicks = new JLabel(" / 00:00.0000 (MM:ss:ms)"); timingPanel.add(maxTicks); timingPanel.add(Box.createHorizontalGlue()); add(timingPanel); // scroll pane scrollPanel = new JPanel(); scrollPanel.setLayout(new BoxLayout(scrollPanel, BoxLayout.PAGE_AXIS)); scrollPane = new JScrollPane(scrollPanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.addMouseWheelListener(this); scrollPane.setWheelScrollingEnabled(false); scrollPane.getHorizontalScrollBar().addAdjustmentListener(this); scrollPane.setPreferredSize(new Dimension(800, 400)); add(scrollPane); // slider interf.timeSlider = new JSlider(JSlider.HORIZONTAL, 0, 0, 0); interf.timeSlider.setMajorTickSpacing(1000); interf.timeSlider.setMinorTickSpacing(500); interf.timeSlider.setPaintTicks(true); interf.timeSlider.setAlignmentX(LEFT_ALIGNMENT); interf.timeSlider.setEnabled(false); interf.timeSlider.setUI(new CustomSliderUI(interf.timeSlider)); interf.timeSlider.addChangeListener(this); scrollPanel.add(interf.timeSlider); // timeline listModel = new DefaultListModel<>(); for(Music music : interf.musicList.getList()) listModel.addElement(music); interf.timeLineList = new TimeLineJList(listModel, interf); musicRenderer = new MusicRenderer(this); interf.timeLineList.setCellRenderer(musicRenderer); interf.timeLineList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); interf.timeLineList.setAlignmentX(LEFT_ALIGNMENT); scrollPanel.add(interf.timeLineList); } public double getZoomLevel() { return zoomLevel; } public void updateList() { final MusicList musicList = interf.musicList; listModel.clear(); for(Music music : musicList.getList()) listModel.addElement(music); // compute max slider long maxTick = 0; for(Music music : musicList.getList()) { if(music.getMusicPattern().getStop() > maxTick) maxTick = music.getMusicPattern().getStop(); } interf.timeSlider.setMaximum((int) maxTick); // update need a delay and have to be call with another thread (note sure why...) new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if(musicList.getList().size() != 0) updateTimeLine(0); } }).start(); } public void updateTick() { currentTick.setText(Util.tickToText(interf.tick)); long maxTick = 0; for(PatternJPanel pattern : musicRenderer.patterns) { pattern.refreshPreferedSize(); long tick = pattern.getMusic().getMusicPattern().getStop(); if(tick > maxTick) maxTick = tick; } maxTicks.setText(Util.tickToText(maxTick) + " (mm:ss:ms)"); } public void mouseWheelMoved(MouseWheelEvent e) { if(e.getSource() == scrollPane) { if (e.isControlDown()) { if(interf.musicList.getList().size() != 0) updateTimeLine(e.getWheelRotation()); } else if(e.isShiftDown()) { JScrollBar sb = scrollPane.getHorizontalScrollBar(); sb.setValue(sb.getValue() + e.getWheelRotation() * SCROLL_SPEED_HORIZONTAL); } else { JScrollBar sb = scrollPane.getVerticalScrollBar(); sb.setValue(sb.getValue() + e.getWheelRotation() * SCROLL_SPEED_VERTICAL); } } } public void updateTimeLine(int wheelRot) { if(wheelRot < 0) zoomLevel *= (double) wheelRot * -1.1; else if (wheelRot > 0) zoomLevel *= (double) wheelRot / 1.1; interf.timeSlider.setEnabled(musicRenderer.patterns.size() != 0); int maxWidth = 0; if(musicRenderer.patterns.size() == 0) { interf.timeSlider.setValue(0); maxWidth = (int) (scrollPane.getPreferredSize().width - new JScrollBar().getPreferredSize().getWidth()) - 1; } else { for(PatternJPanel pattern : musicRenderer.patterns) { pattern.refreshPreferedSize(); int width = pattern.getEnd(); if(width > maxWidth) maxWidth = width; } } interf.timeSlider.setMaximumSize(new Dimension(maxWidth, interf.timeSlider.getPreferredSize().height)); interf.timeLineList.setMaximumSize(new Dimension(maxWidth, PatternJPanel.PATTERN_HEIGHT * musicRenderer.patterns.size())); interf.timeLineList.setPreferredSize(new Dimension(maxWidth, PatternJPanel.PATTERN_HEIGHT * musicRenderer.patterns.size())); scrollPanel.setPreferredSize(new Dimension(maxWidth, PatternJPanel.PATTERN_HEIGHT * musicRenderer.patterns.size())); scrollPane.repaint(); scrollPane.getViewport().revalidate(); scrollPanel.revalidate(); } @Override public void focusLost(FocusEvent e) { if(e.getSource() == currentTick) { interf.tick = Util.textToTick(currentTick.getText()); currentTick.setText(Util.tickToText(interf.tick)); interf.computeTick(); } } @Override public void focusGained(FocusEvent e) { if(e.getSource() == currentTick) currentTick.setCaretPosition(currentTick.getDocument().getLength()); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource() == currentTick) interf.getContentPane().requestFocus(); } @Override public void stateChanged(ChangeEvent e) { if(e.getSource() == interf.timeSlider) { interf.tick = interf.timeSlider.getValue(); interf.computeTick(); } } @Override public void adjustmentValueChanged(AdjustmentEvent e) { if(e.getSource() == scrollPane.getHorizontalScrollBar()) { musicRenderer.setInfoPadding(scrollPane.getHorizontalScrollBar().getValue()); scrollPane.repaint(); scrollPane.getViewport().revalidate(); scrollPanel.revalidate(); } } }