/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk.ui.widgets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.text.DecimalFormat; import java.util.HashSet; import java.util.Set; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; import org.jajuk.events.JajukEvent; import org.jajuk.events.JajukEvents; import org.jajuk.events.ObservationManager; import org.jajuk.events.Observer; import org.jajuk.services.players.Player; import org.jajuk.services.players.QueueModel; import org.jajuk.ui.helpers.JajukTimer; import org.jajuk.util.Conf; import org.jajuk.util.Const; import org.jajuk.util.Messages; import org.jajuk.util.UtilFeatures; import org.jajuk.util.UtilString; import org.jajuk.util.log.Log; /** * . */ public class TrackPositionSliderToolbar extends JPanel implements ChangeListener, MouseWheelListener, Observer { /** Generic playing position toolbar, used in information panel a full screen. */ private static final long serialVersionUID = 1L; /** Last slider manual move date. */ private long lDateLastAdjust; private JSlider jsPosition; private JLabel jlCurrent; String sCurrentStatus; /** Swing Timer to refresh the component. */ private final Timer timer = new Timer(JajukTimer.D_MS_HEARTBEAT, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { update(new JajukEvent(JajukEvents.HEART_BEAT)); } catch (Exception ex) { Log.error(ex); } } }); /** * Instantiates a new track position slider toolbar. */ public TrackPositionSliderToolbar() { super(); initGui(); // check if some errors occurred before the view has been displayed if (ObservationManager.containsEvent(JajukEvents.PLAY_ERROR)) { update(new JajukEvent(JajukEvents.PLAY_ERROR, ObservationManager.getDetailsLastOccurence(JajukEvents.PLAY_ERROR))); } // check if some track has been launched before the view has been // displayed UtilFeatures.updateStatus(this); // register for given events ObservationManager.register(this); timer.start(); } /** * Inits the gui. */ private void initGui() { setLayout(new MigLayout("ins 0 5 0 5", "[70%,fill][30%,grow]")); setToolTipText(Messages.getString("InformationJPanel.7")); jsPosition = new JSlider(0, 100, 0); jsPosition.addChangeListener(this); jsPosition.setOpaque(false); jsPosition.addMouseWheelListener(this); jsPosition.setEnabled(false); jlCurrent = new JLabel(); jlCurrent.setToolTipText(Messages.getString("CommandJPanel.15")); jlCurrent.addMouseListener(new TimeDisplaySwitchMouseAdapter()); add(jsPosition, "grow"); add(jlCurrent, "grow,left"); } /* * (non-Javadoc) * * @see * javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent * ) */ @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == jsPosition && !jsPosition.getValueIsAdjusting()) { lDateLastAdjust = System.currentTimeMillis(); setPosition((float) jsPosition.getValue() / 100); } } /** * Call a seek. * * @param fPosition */ private void setPosition(final float fPosition) { new Thread("TrackSlider Position Thread") { @Override public void run() { Player.seek(fPosition); } }.start(); } /* * (non-Javadoc) * * @seejava.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event. * MouseWheelEvent) */ @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.getSource() == jsPosition) { int iOld = jsPosition.getValue(); int iNew = iOld - (e.getUnitsToScroll() * 3); jsPosition.setValue(iNew); } } /** * Gets the current position. * * @return Position value */ public int getCurrentPosition() { return this.jsPosition.getValue(); } /** * Set the current status for current track ex : 01:01:01/02:02:02. * * @param lTime * @param length */ public final void setCurrentTimeMessage(long lTime, long length) { String string; int timeFormat = 0; // Set the required decimal precision for percentage here DecimalFormat df = new DecimalFormat("0"); // (0.##) for 2 decimal places try { timeFormat = Conf.getInt(Const.CONF_FORMAT_TIME_ELAPSED); } catch (Exception e) { Log.debug(e); } float lTimePercent = 0f; if (lTime > 0) { lTimePercent = (float) ((float) lTime / (float) length * 100.0); } switch (timeFormat) { /* * same as default... case 0: { string = UtilString.formatTimeBySec(lTime) + * " / " + UtilString.formatTimeBySec(length); break; } */ case 1: { string = "-" + UtilString.formatTimeBySec(length - lTime) + " / " + UtilString.formatTimeBySec(length); break; } case 2: { string = df.format(lTimePercent) + " % / " + UtilString.formatTimeBySec(length); break; } case 3: { string = df.format(lTimePercent - 100f) + " % / " + UtilString.formatTimeBySec(length); break; } default: { string = UtilString.formatTimeBySec(lTime) + " / " + UtilString.formatTimeBySec(length); } } sCurrentStatus = string; jlCurrent.setText(string); } /* * (non-Javadoc) * * @see org.jajuk.events.Observer#getRegistrationKeys() */ @Override public Set<JajukEvents> getRegistrationKeys() { Set<JajukEvents> eventSubjectSet = new HashSet<JajukEvents>(); eventSubjectSet.add(JajukEvents.ZERO); eventSubjectSet.add(JajukEvents.PLAY_ERROR); eventSubjectSet.add(JajukEvents.PLAYER_PAUSE); eventSubjectSet.add(JajukEvents.PLAYER_RESUME); eventSubjectSet.add(JajukEvents.PLAYER_STOP); return eventSubjectSet; } /* * (non-Javadoc) * * @see org.jajuk.events.Observer#update(org.jajuk.events.JajukEvent) */ @Override public final void update(final JajukEvent event) { final JajukEvents subject = event.getSubject(); // do not insert this subject inside the invokeLater because we have to // leave the awt dispatcher called inside the setMessage and THEN, sleep // for 2 secs. if (JajukEvents.PLAY_ERROR.equals(subject)) { try { // we synchronize this code to make sure error message is // visible all 2 secs synchronized (this) { // reset data setCurrentTimeMessage(0, 0); } } catch (Exception e) { Log.error(e); } } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (JajukEvents.HEART_BEAT.equals(subject) && !QueueModel.isStopped() && !Player.isPaused()) { long length = JajukTimer.getInstance().getCurrentTrackTotalTime(); long lTime = JajukTimer.getInstance().getCurrentTrackEllapsedTime(); int iPos = (int) (100 * Player.getCurrentPosition()); setCurrentTimeMessage(lTime, length); // Make sure to enable the slider if (!jsPosition.isEnabled()) { jsPosition.setEnabled(true); } // if position is adjusting, don't disturb user if (jsPosition.getValueIsAdjusting() || Player.isSeeking()) { return; } // make sure not to set to old position if ((System.currentTimeMillis() - lDateLastAdjust) < 2000) { return; } // remove and re-add listener to make sure not to add it // twice jsPosition.removeChangeListener(TrackPositionSliderToolbar.this); jsPosition.setValue(iPos); jsPosition.addChangeListener(TrackPositionSliderToolbar.this); } else if (JajukEvents.ZERO.equals(subject) || JajukEvents.PLAYER_STOP.equals(subject)) { setCurrentTimeMessage(0, 0); jsPosition.setEnabled(false); jsPosition.removeMouseWheelListener(TrackPositionSliderToolbar.this); jsPosition.removeChangeListener(TrackPositionSliderToolbar.this); // use set value, not setPosition that would cause // a seek that could fail with some formats jsPosition.setValue(0); jsPosition.addMouseWheelListener(TrackPositionSliderToolbar.this); jsPosition.addChangeListener(TrackPositionSliderToolbar.this); } else if (JajukEvents.PLAYER_PAUSE.equals(subject)) { jsPosition.setEnabled(false); jsPosition.removeMouseWheelListener(TrackPositionSliderToolbar.this); jsPosition.removeChangeListener(TrackPositionSliderToolbar.this); } else if (JajukEvents.PLAYER_RESUME.equals(subject)) { // Avoid adding listeners twice if (jsPosition.getMouseWheelListeners().length == 0) { jsPosition.addMouseWheelListener(TrackPositionSliderToolbar.this); } if (jsPosition.getChangeListeners().length == 0) { jsPosition.addChangeListener(TrackPositionSliderToolbar.this); } jsPosition.setEnabled(true); } } }); } } /** * Gets the current status message. * * @return the current status message */ public String getCurrentStatusMessage() { return sCurrentStatus; } /** * Small MouseAdapter to loop through the different ways of displaying the * elapsed time. */ private final class TimeDisplaySwitchMouseAdapter extends MouseAdapter { /* (non-Javadoc) * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent) */ @Override public void mouseClicked(MouseEvent e) { int currentFormat = Conf.getInt(Const.CONF_FORMAT_TIME_ELAPSED); Conf.setProperty(Const.CONF_FORMAT_TIME_ELAPSED, Integer.toString(((currentFormat + 1) % Const.FORMAT_TIME_ELAPSED_MAX))); } } }