/*
* org.openmicroscopy.shoola.agents.imviewer.util.player.MoviePlayerControl
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2015 University of Dundee. All rights reserved.
*
*
* 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
* (at your option) 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.imviewer.util.player;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractButton;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openmicroscopy.shoola.util.CommonsLangUtils;
import org.openmicroscopy.shoola.util.ui.NumericalTextField;
import org.openmicroscopy.shoola.util.ui.slider.TwoKnobsSlider;
/**
* The movie player controller.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Andrea Falconi
* <a href="mailto:a.falconi@dundee.ac.uk">a.falconi@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* <small>
* (<b>Internal version:</b> $Revision: $ $Date: $)
* </small>
* @since OME2.2
*/
class MoviePlayerControl
implements ActionListener, ChangeListener, FocusListener,
PropertyChangeListener
{
/** Identifies the play movie action. */
private static final int PLAY_CMD = 4;
/** Identifies the play pause action. */
private static final int PAUSE_CMD = 5;
/** Identifies the stop movie action. */
private static final int STOP_CMD = 6;
/** Identifies the change of movie type. */
private static final int MOVIE_TYPE_CMD = 7;
/**
* Action command ID to sync JSpinner and the text field editor.
*/
private static final int EDITOR_CMD = 8;
/** Indicates that the movie is played across z-sections. */
private static final int ACROSS_Z_CMD = 9;
/** Indicates that the movie is played across timepoints. */
private static final int ACROSS_T_CMD = 10;
/** Indicates that the movie is played across z-sections and timepoints. */
private static final int ACROSS_ZT_CMD = 11;
/** Reference to the View. */
private MoviePlayer model;
/** Reference to the View. */
private MoviePlayerUI view;
/** Adds an {@link ActionListener} to an {@link AbstractButton}.
*
* @param button The component to attach the listener to.
* @param id The action command ID.
*/
private void attachButtonListener(AbstractButton button, int id)
{
button.addActionListener(this);
button.setActionCommand(""+id);
}
/**
* Adds listeners to a {@link JTextField}.
*
* @param field The component to attach the listeners to.
*/
private void attachFieldListeners(JTextField field)
{
field.addFocusListener(this);
field.addPropertyChangeListener(this);
}
/** Adds listeners to the UI components. */
private void initListeners()
{
JTextField editor = view.editor;
editor.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
editorActionHandler();
}
});
//JButton
attachButtonListener(view.play, PLAY_CMD);
attachButtonListener(view.pause, PAUSE_CMD);
attachButtonListener(view.stop, STOP_CMD);
//JComboBox
JComboBox box = view.movieTypes;
box.setActionCommand(""+MOVIE_TYPE_CMD);
box.addActionListener(this);
//JSpinner
view.fps.addChangeListener(this);
//MoviePane
attachFieldListeners(view.startT);
attachFieldListeners(view.endT);
attachFieldListeners(view.startZ);
attachFieldListeners(view.endZ);
attachButtonListener(view.acrossZ, ACROSS_Z_CMD);
attachButtonListener(view.acrossT, ACROSS_T_CMD);
//attachButtonListener(view.acrossZT, ACROSS_ZT_CMD);
view.tSlider.addPropertyChangeListener(this);
view.zSlider.addPropertyChangeListener(this);
}
/**
* Checks if the delay entered for the timer is valid.
* If so, sets the value and updates the UI.
*/
private void editorActionHandler()
{
int val = ((Integer) view.fps.getValue()).intValue();
Number n = view.editor.getValueAsNumber();
if (n != null) {
val = n.intValue();
model.setTimerDelay(val);
view.setTimerDelay(val);
} else {
view.editor.setText(""+val);
model.setTimerDelay(val);
view.setTimerDelay(val);
}
}
/**
* Creates a new instance.
*
* @param model Reference to the Model. Mustn't be <code>null</code>.
* @param view Reference to the View. Mustn't be <code>null</code>.
*/
MoviePlayerControl(MoviePlayer model, MoviePlayerUI view)
{
if (model == null) throw new IllegalArgumentException("No model.");
if (view == null) throw new IllegalArgumentException("No view.");
this.view = view;
this.model = model;
initListeners();
}
/**
* Sets the values required to play the movie. Each time a value is set
* and the movie is playing, the movie is first stopped.
* @see ActionListener#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent ae)
{
try {
int index = Integer.parseInt(ae.getActionCommand());
switch (index) {
case ACROSS_T_CMD:
if (view.acrossZ.isSelected()) {
if (view.acrossT.isSelected())
model.setMovieIndex(MoviePlayerDialog.ACROSS_ZT);
else
model.setMovieIndex(MoviePlayerDialog.ACROSS_Z);
} else {
if (view.acrossT.isSelected())
model.setMovieIndex(MoviePlayerDialog.ACROSS_T);
else
view.acrossT.setSelected(true);
}
break;
case ACROSS_Z_CMD:
if (view.acrossT.isSelected()) {
if (view.acrossZ.isSelected())
model.setMovieIndex(MoviePlayerDialog.ACROSS_ZT);
else
model.setMovieIndex(MoviePlayerDialog.ACROSS_T);
} else {
if (view.acrossZ.isSelected())
model.setMovieIndex(MoviePlayerDialog.ACROSS_Z);
else
view.acrossZ.setSelected(true);
}
break;
case ACROSS_ZT_CMD:
model.setMovieIndex(MoviePlayerDialog.ACROSS_ZT);
break;
case PLAY_CMD:
model.setPlayerState(Player.START);
break;
case PAUSE_CMD:
model.setPlayerState(Player.PAUSE);
break;
case STOP_CMD:
model.setPlayerState(Player.STOP);
break;
case EDITOR_CMD:
editorActionHandler();
break;
case MOVIE_TYPE_CMD:
int i = ((JComboBox) ae.getSource()).getSelectedIndex();
model.setMovieType(view.getMovieType(i));
break;
}
} catch(NumberFormatException nfe) {
throw new Error("Invalid Action ID "+ae.getActionCommand(), nfe);
}
}
/**
* Sets the timer delay.
* @see ChangeListener#stateChanged(ChangeEvent)
*/
public void stateChanged(ChangeEvent e)
{
int v = ((Integer) view.fps.getValue()).intValue();
model.setTimerDelay(v);
view.setTimerDelay(v);
}
/**
* Handles the lost of focus on the various text fields.
* If focus is lost while editing, then we don't consider the text
* currently displayed in the text field and we reset it to the current
* value.
* @see FocusListener#focusLost(FocusEvent)
*/
public void focusLost(FocusEvent e)
{
String edit = view.editor.getText(),
ed = ""+model.getTimerDelay();
if (edit == null || !edit.equals(ed))
view.editor.setText(ed);
String startT = ""+(model.getStartT()+1);
String endT = ""+(model.getEndT()+1);
String startVal = view.startT.getText(), endVal = view.endT.getText();
if (CommonsLangUtils.isBlank(startVal) || !startVal.equals(startT))
view.startT.setText(startT);
if (CommonsLangUtils.isBlank(endVal) || !endVal.equals(endT))
view.endT.setText(endT);
String startZ = ""+(model.getStartZ()+1);
String endZ = ""+(model.getEndZ()+1);
startVal = view.startZ.getText();
endVal = view.endZ.getText();
if (CommonsLangUtils.isBlank(startVal) || !startVal.equals(startZ))
view.startZ.setText(startZ);
if (CommonsLangUtils.isBlank(endVal) || !endVal.equals(endZ))
view.endZ.setText(endZ);
}
/**
* Sets the z-section interval and timepoint interval.
* @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt)
{
String name = evt.getPropertyName();
Object source = evt.getSource();
int s = -1;
int e = -1;
if (TwoKnobsSlider.LEFT_MOVED_PROPERTY.equals(name)) {
if (source.equals(view.zSlider)) {
s = view.zSlider.getStartValueAsInt();
model.setStartZ(s);
view.setStartZ(s);
} else if (source.equals(view.tSlider)) {
s = view.tSlider.getStartValueAsInt();
model.setStartT(s);
view.setStartT(s);
}
} else if (TwoKnobsSlider.RIGHT_MOVED_PROPERTY.equals(name)) {
if (source.equals(view.zSlider)) {
e = view.zSlider.getEndValueAsInt();
model.setEndZ(e);
view.setEndZ(e);
} else if (source.equals(view.tSlider)) {
e = view.tSlider.getEndValueAsInt();
model.setEndT(e);
view.setEndT(e);
}
} else if (NumericalTextField.TEXT_UPDATED_PROPERTY.equals(name)) {
Number n;
if (source.equals(view.startT)) {
n = view.startT.getValueAsNumber();
if (n == null) return;
s = n.intValue()-1;
if (s >= model.getEndT() || s < 0) return;
model.setStartT(s);
view.tSlider.setStartValue(s);
} else if (source.equals(view.startZ)) {
n = view.startZ.getValueAsNumber();
if (n == null) return;
s = n.intValue()-1;
if (s >= model.getEndZ() || s < 0) return;
model.setStartZ(s);
view.zSlider.setStartValue(s);
} else if (source.equals(view.endZ)) {
n = view.endZ.getValueAsNumber();
if (n == null) return;
s = n.intValue()-1;
if (s <= model.getStartZ() || s > model.getMaxZ()) return;
model.setEndZ(s);
view.zSlider.setEndValue(s);
} else if (source.equals(view.endT)) {
n = view.endT.getValueAsNumber();
if (n == null) return;
s = n.intValue()-1;
if (s <= model.getStartT() || s > model.getMaxT()) return;
model.setEndT(s);
view.tSlider.setEndValue(s);
}
}
}
/**
* Required by {@link FocusListener} I/F but not actually needed in
* our case, no operation implementation.
* @see FocusListener#focusGained(FocusEvent)
*/
public void focusGained(FocusEvent e) {}
}