package org.korsakow.ide.ui.components; /** * */ import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.korsakow.ide.Application; import org.korsakow.ide.exception.MediaRuntimeException; import org.korsakow.ide.resources.media.Playable; import org.korsakow.ide.resources.media.PlayableImage; import org.korsakow.ide.resources.media.PlayableVideo; import org.korsakow.ide.resources.media.UnsupportedVideo; import org.korsakow.ide.ui.components.layout.VerticalFlowLayout; import org.korsakow.ide.util.UIResourceManager; import org.korsakow.ide.util.UIUtil; /** * Replaces MediaPanel. Rename classes after the swap. * @author d * */ public class NewMediaPanel extends JPanel // extends ResourcePanel<M> { private static final int SLIDER_MAX = 100*1000; private static final String TIME_FORMAT_REGEX = "(?:(\\d+):)?(\\d{1,2})(?:[.](\\d{1,3}))?"; public static String formatTime(long millis) { long ms = millis%100; long s = (long)(Math.floor(millis/1000.0)%60); long m = (long)(Math.floor(millis/1000.0/60.0)); return String.format("%02d:%02d.%02d", m, s, ms); } /** * @see TIME_FORMAT_REGEX * @return null if improperly formatted */ public static Long parseTime(String time) { if (!time.matches(TIME_FORMAT_REGEX)) return null; long m; long s; long ms; try { m = Long.parseLong(time.replaceAll(TIME_FORMAT_REGEX, "$1")); } catch (NumberFormatException e) { m = 0; } try { ms = Long.parseLong(time.replaceAll(TIME_FORMAT_REGEX, "$3")); } catch (NumberFormatException e) { ms = 0; } try { s = Long.parseLong(time.replaceAll(TIME_FORMAT_REGEX, "$2")); } catch (NumberFormatException e) { return null; } return m*1000*60 + s*1000 + ms; } protected JLabel loadingLabel; protected Playable playable; protected Component mediaComponent; protected JPanel mediaPlaceHolder; protected JButton playButton; protected JButton stopButton; protected JButton seekStartButton; protected JButton seekEndButton; protected JSlider seekSlider; protected JPanel buttonPanel; protected JLabel timeLabel; protected Timer seekSliderTimer; protected boolean seekSliderDontUpdate = false; protected boolean isTemporal = false; private long duration; public NewMediaPanel() { initUI(); initListeners(); } protected void initUI() { setLayout(new BorderLayout()); mediaPlaceHolder = new JPanel(null); mediaPlaceHolder.setBackground(Color.black); add(mediaPlaceHolder, BorderLayout.CENTER); loadingLabel = new JLabel(UIResourceManager.getIcon("Throbber_allbackgrounds_circledots_32.gif")); loadingLabel.setBounds(0, 0, 32, 32); mediaPlaceHolder.add(loadingLabel); seekSlider = new JSlider(JSlider.HORIZONTAL, 0, SLIDER_MAX, 0); seekSlider.setPaintLabels(false); seekSlider.setPaintTicks(false); seekSlider.setPaintTrack(true); timeLabel = new JLabel(); timeLabel.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10)); timeLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); timeLabel.setText(formatTime(0)); // mediaPanel.add(seekSlider); buttonPanel = new JPanel(new BorderLayout(0, 0)); add(buttonPanel, BorderLayout.SOUTH); seekStartButton = new JButton(UIResourceManager.getIcon(UIResourceManager.ICON_CONTROL_SEEK_START)); // buttonPanel.add(seekStartButton); JPanel playPanel = new JPanel(new CardLayout()); buttonPanel.add(playPanel, BorderLayout.WEST); playButton = new JButton(UIResourceManager.getIcon(UIResourceManager.ICON_CONTROL_PLAY)); playButton.setBorderPainted(false); playButton.setContentAreaFilled(false); playPanel.add(playButton, "play"); stopButton = new JButton(UIResourceManager.getIcon(UIResourceManager.ICON_CONTROL_STOP)); stopButton.setBorderPainted(false); stopButton.setContentAreaFilled(false); playPanel.add(stopButton, "stop"); stopButton.setVisible(false); seekEndButton = new JButton(UIResourceManager.getIcon(UIResourceManager.ICON_CONTROL_SEEK_END)); // buttonPanel.add(seekEndButton); VerticalFlowLayout flowLayout = new VerticalFlowLayout(VerticalFlowLayout.CENTER, 0, 0); flowLayout.setMaximizeOtherDimension(true); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(seekSlider); panel.add(timeLabel); buttonPanel.add(panel, BorderLayout.CENTER); playPanel.setPreferredSize(new Dimension(playButton.getIcon().getIconWidth(), playButton.getIcon().getIconHeight())); playPanel.setMaximumSize(new Dimension(playButton.getIcon().getIconWidth(), playButton.getIcon().getIconHeight())); } protected void initListeners() { playButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { playable.start(); playButton.setVisible(false); stopButton.setVisible(true); // seekSliderTimer.start(); } }); stopButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { playable.stop(); playButton.setVisible(true); stopButton.setVisible(false); // seekSliderTimer.stop(); } }); seekStartButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { playable.setTime(0); } }); seekEndButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { playable.setTime( duration ); } }); seekSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (playable == null) return; final long time = (long)(seekSlider.getValue()*duration/(double)SLIDER_MAX); timeLabel.setText(formatTime(time)); if (seekSliderDontUpdate) return; playable.setTime(time); } }); seekSliderTimer = new Timer(100, new ActionListener() { public void actionPerformed(ActionEvent e) { onSliderTimer(); } }); } public void addSeekSliderChangeListener(ChangeListener listener) { seekSlider.addChangeListener(listener); } public void setControlsEnabled(boolean enabled) { playButton.setEnabled(enabled); stopButton.setEnabled(enabled); seekStartButton.setEnabled(enabled); seekEndButton.setEnabled(enabled); seekSlider.setEnabled(enabled); } public void setTemporalControlsVisible(boolean enabled) { playButton.setVisible(enabled); stopButton.setVisible(false); seekStartButton.setVisible(enabled); seekEndButton.setVisible(enabled); seekSlider.setVisible(enabled); } public void setPlayable(Playable newPlayable) { isTemporal = false; setControlsEnabled(false); setMediaComponent(null); // DO THIS BEFORE DISPOSING THE PLAYABLE OR IT WONT GC RIGHT seekSliderTimer.stop(); setTemporalControlsVisible(false); if (playable != null) { playable.dispose(); playable = null; } if (newPlayable != null) { playable = newPlayable; playable.setTime(0); // not sure why this is necessary sometimes Component component; try { component = playable.getComponent(); } catch (MediaRuntimeException e) { component = new UnsupportedVideo("").getComponent(); } setMediaComponent(component); setControlsEnabled(true); isTemporal = playable.isTemporal(); setTemporalControlsVisible(isTemporal); duration = playable.getDuration(); } else { } seekSlider.setValue(0); // if (isTemporal) if (!seekSliderTimer.isRunning()) seekSliderTimer.start(); // else // seekSliderTimer.stop(); } protected void setMediaComponent(final Component mediaComponent) { if (this.mediaComponent != null) { this.mediaComponent.getParent().remove(this.mediaComponent); } this.mediaComponent = mediaComponent; if (this.mediaComponent != null) { mediaPlaceHolder.add(this.mediaComponent); } if (mediaComponent != null && playable != null) { // runLater: blarg, we have to wait for mediaPlaceHolder to have been layed out so its size is calculated. this is a lazy and not-the-best-way of doing it UIUtil.runUITaskLater(new Runnable() { public void run() { try { Dimension maxSize = mediaPlaceHolder.getSize(); Dimension size = mediaComponent.getSize(); if (playable instanceof PlayableVideo) { size = ((PlayableVideo)playable).getAspectRespectingDimension(mediaPlaceHolder.getSize()); } else if (playable instanceof PlayableImage) { size = ((PlayableImage)playable).getAspectRespectingDimension(mediaPlaceHolder.getSize()); } mediaComponent.setBounds((maxSize.width-size.width)/2, (maxSize.height-size.height)/2, size.width, size.height); loadingLabel.setBounds(mediaComponent.getBounds()); } catch (MediaRuntimeException e) { Application.getInstance().showHandledErrorDialog("general.errors.unsupportedplaybackformat.title", "general.errors.unsupportedplaybackformat.message"); } } }); } repaint(); revalidate(); } private Component getMediaComponent() { return mediaComponent; } private void setMediaTime(long time) { if (!isTemporal) throw new IllegalArgumentException("cant set time of non-temporal media"); playable.setTime(time); seekSliderDontUpdate = true; long duration = playable.getDuration(); long value = SLIDER_MAX*playable.getTime() / (duration!=0?duration:1); seekSlider.setValue((int)value); seekSliderDontUpdate = false; } private long getMediaTime() { if (!isTemporal) throw new IllegalArgumentException("cant get time from non-temporal media"); return playable.getTime(); } private void onSliderTimer() { if (playable == null) return; seekSliderDontUpdate = true; long value = (long)Math.ceil(SLIDER_MAX*playable.getTime() / ((double)(duration!=0?duration:1))); seekSlider.setValue((int)value); seekSliderDontUpdate = false; } private JSlider getSeekSlider() { return seekSlider; } public Playable getPlayable() { return playable; } public void setDuration( long duration ) { this.duration = duration; } public void dispose() { setPlayable(null); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); UIUtil.setChildrenEnabled(this, enabled); loadingLabel.setVisible(!enabled); } @Override public void doLayout() { super.doLayout(); loadingLabel.setBounds(getWidth()/2 - 32/3, getHeight()/2 - 32/2, 32, 32); } }