/* * org.openmicroscopy.shoola.agents.imviewer.util.player.MoviePlayerUI * *------------------------------------------------------------------------------ * Copyright (C) 2006-2014 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; //Java imports import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.SpinnerNumberModel; import javax.swing.border.TitledBorder; //Third-party libraries //Application-internal dependencies import org.openmicroscopy.shoola.agents.imviewer.IconManager; import org.openmicroscopy.shoola.util.ui.NumericalTextField; import org.openmicroscopy.shoola.util.ui.UIUtilities; import org.openmicroscopy.shoola.util.ui.slider.TwoKnobsSlider; /** * The UI delegate. * * @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 MoviePlayerUI extends JPanel { /** The maximum value for which the ticks are painted. */ private static final int MAX_RANGE = 100; /** UI identifier corresponding to {@link MoviePlayer#LOOP}. */ private static final int LOOP_CMD = 0; /** UI identifier corresponding to {@link MoviePlayer#LOOP_BACKWARD}. */ private static final int LOOP_BACKWARD_CMD = 1; /** UI identifier corresponding to {@link MoviePlayer#BACKWARD}. */ private static final int BACKWARD_CMD = 2; /** UI identifier corresponding to {@link MoviePlayer#FORWARD}. */ private static final int FORWARD_CMD = 3; /** UI identifier corresponding to {@link MoviePlayer#PINGPONG}. */ private static final int PINGPONG_CMD = 4; /** Movie type selections. */ private static final String[] selections; /** Width of a character w.r.t the font metrics. */ private int charWidth; /** Reference to the model. */ private MoviePlayer model; /** Tool bar hosting the buttons. */ private JToolBar toolBar; /** The button to play the movie. */ JButton play; /** The button to pause the movie. */ JButton pause; /** The button to pause the movie. */ JButton stop; /** To specify the movie playback rate in frames per second. */ JSpinner fps; /** To define new editor for JSpinner (due to JSpinner bug). */ NumericalTextField editor; /** To select the movie type. */ JComboBox movieTypes; /** Field hosting the start z-section. */ NumericalTextField startZ; /** Field hosting the end z-section. */ NumericalTextField endZ; /** Field hosting the start timepoint. */ NumericalTextField startT; /** Field hosting the end timepoint. */ NumericalTextField endT; /** Box to select to play the movie across z-section. */ JCheckBox acrossZ; /** Box to select to play the movie across timepoint. */ JCheckBox acrossT; /** Box to select to play the movie across z-section and timepoint. */ //JRadioButton acrossZT; /** Two knobs slider to select the z-section interval. */ TwoKnobsSlider zSlider; /** Two knobs slider to select the timepoint interval. */ TwoKnobsSlider tSlider; /** Initializes the static components. */ static { selections = new String[5]; selections[LOOP_CMD] = "Loop"; selections[LOOP_BACKWARD_CMD] = "Loop Backward"; selections[BACKWARD_CMD] = "Backward"; selections[FORWARD_CMD] = "Forward"; selections[PINGPONG_CMD] = "Back and Forth"; } /** * Returns the UI index corresponding to the specified movie type. * * @param v The movie type * @return See above. */ private int getMovieTypeIndex(int v) { switch (v) { case MoviePlayer.LOOP: return LOOP_CMD; case MoviePlayer.BACKWARD: return BACKWARD_CMD; case MoviePlayer.FORWARD: return FORWARD_CMD; case MoviePlayer.PINGPONG: return PINGPONG_CMD; case MoviePlayer.LOOP_BACKWARD: return LOOP_BACKWARD_CMD; } throw new IllegalArgumentException("Movie type not supported."); } /** Initializes the components composing the display. */ private void initComponents() { IconManager icons = IconManager.getInstance(); //movie controls play = new JButton(icons.getIcon(IconManager.PLAY)); play.setToolTipText(UIUtilities.formatToolTipText("Play movie.")); pause = new JButton(icons.getIcon(IconManager.PAUSE)); pause.setToolTipText(UIUtilities.formatToolTipText("Pause.")); stop = new JButton(icons.getIcon(IconManager.STOP)); stop.setToolTipText(UIUtilities.formatToolTipText("Stop movie.")); movieTypes = new JComboBox(selections); movieTypes.setSelectedIndex(getMovieTypeIndex(model.getMovieType())); //Spinner timepoint granularity is 1, so must be stepSize int max = model.getMaximumTimer(); fps = new JSpinner(new SpinnerNumberModel(model.getTimerDelay(), MoviePlayer.FPS_MIN, max, 1)); String s = "Select or enter the movie playback rate " + "(frames per second)."; editor = new NumericalTextField(MoviePlayer.FPS_MIN, max, Integer.class); editor.setText(""+model.getTimerDelay()); editor.setToolTipText(s); editor.setColumns((""+max).length()); fps.setEditor(editor); //movie selection int maxZ = model.getMaxZ(); int minZ = model.getMinZ(); int rangeZ = maxZ-minZ; zSlider = new TwoKnobsSlider(minZ, maxZ, model.getStartZ(), model.getEndZ()); zSlider.setPaintEndLabels(false); zSlider.setPaintLabels(false); if (rangeZ <= 0 || rangeZ > MAX_RANGE) zSlider.setPaintTicks(false); int length = (""+(maxZ+1)).length(); startZ = new NumericalTextField(minZ+1, maxZ+1); startZ.setText(""+(model.getStartZ()+1)); startZ.setColumns(length); startZ.setToolTipText( UIUtilities.formatToolTipText("Enter the start z-section.")); endZ = new NumericalTextField(minZ+1, maxZ+1); endZ.setText(""+(model.getEndZ()+1)); endZ.setColumns(length); endZ.setToolTipText( UIUtilities.formatToolTipText("Enter the end z-section.")); int maxT = model.getMaxT(); int minT = model.getMinT(); int rangeT = maxT-minT; tSlider = new TwoKnobsSlider(minT, maxT, model.getStartT(), model.getEndT()); tSlider.setPaintEndLabels(false); tSlider.setPaintLabels(false); if (rangeT <= 0 || rangeT > MAX_RANGE) tSlider.setPaintTicks(false); length = (""+(maxT+1)).length(); startT = new NumericalTextField(minT+1, maxT+1); startT.setText(""+(model.getStartT()+1)); startT.setColumns(length); startT.setToolTipText( UIUtilities.formatToolTipText("Enter the start timepoint.")); endT = new NumericalTextField(minT+1, maxT+1); endT.setText(""+(model.getEndT()+1)); endT.setColumns(length); endT.setToolTipText( UIUtilities.formatToolTipText("Enter the end timepoint.")); acrossZ = new JCheckBox("Across Z"); acrossT = new JCheckBox("Across T"); } /** * Builds the tool bar hosting the buttons. * * @return See above. */ private JToolBar buildToolBar() { toolBar = new JToolBar(); toolBar.setBorder(BorderFactory.createEtchedBorder()); toolBar.setFloatable(false); toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); toolBar.add(play); toolBar.add(stop); return toolBar; } /** * Builds panel with frames per second controls. * * @return See above. */ private JPanel createFPSControls() { JPanel p = new JPanel(); GridBagConstraints c = new GridBagConstraints(); p.setLayout(new GridBagLayout()); JLabel l = new JLabel("Frame Rate"); c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.WEST; p.add(l, c); c.gridx = 1; c.insets = new Insets(0, 10, 0, 0); p.add(fps, c); return p; } /** * Builds a panel containing the plays label and the plays * control combo box. * * @return Panel containing label and combobox for direction controls. */ private JPanel createDirectionControls() { JPanel contain = new JPanel(); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; JLabel l = new JLabel("Play Mode:"); contain.add(l, gbc); gbc.gridx = 1; gbc.insets = new Insets(0, 10, 0, 0); contain.add(movieTypes, gbc); return contain; } /** * Builds a panel wrapping the start and end text. * * @param length The Length of the text fields. * @param start The start textFields. * @param end The end textField. * @return See below. */ private JPanel buildControlsMoviePanel(int length, JTextField start, JTextField end) { JPanel p = new JPanel(); Insets insets = end.getInsets(); int x = insets.left+length*charWidth+insets.left; GridBagConstraints c = new GridBagConstraints(); p.setLayout(new GridBagLayout()); c.weightx = 0; c.anchor = GridBagConstraints.WEST; p.add(new JLabel(" Start "), c); c.gridx = 1; c.ipadx = x; c.weightx = 0.5; p.add(UIUtilities.buildComponentPanel(start), c); c.gridx = 2; c.ipadx = 0; c.weightx = 0; p.add(new JLabel(" End "), c); c.gridx = 3; c.ipadx = x; c.weightx = 0.5; p.add(UIUtilities.buildComponentPanel(end), c); return p; } /** * Builds a panel with a slider, a radio button and JPanel with * controls. * * @param slider The slider to wrap. * @param button The radioButton to wrap. * @param controls The panel to wrap. * @return See below. */ private JPanel buildGroupPanel(TwoKnobsSlider slider, JCheckBox button, JPanel controls) { JPanel p = new JPanel(), group = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); p.add(slider); p.add(controls); group.setLayout(new BoxLayout(group, BoxLayout.X_AXIS)); group.add(button); group.add(p); return group; } /** Builds and lays out the UI. */ private void buildGUI() { // lays out the movie selection JPanel movie = new JPanel(); movie.setBorder(new TitledBorder("Frame Selection")); movie.setLayout(new GridBagLayout()); GridBagConstraints mc = new GridBagConstraints(); mc.insets = new Insets(5, 5, 5, 5); JPanel p = buildGroupPanel(zSlider, acrossZ, buildControlsMoviePanel((""+model.getMaxZ()).length(), startZ, endZ)); mc.gridy = 1; mc.anchor = GridBagConstraints.WEST; movie.add(p, mc); p = buildGroupPanel(tSlider, acrossT, buildControlsMoviePanel((""+model.getMaxT()).length(), startT, endT)); mc.gridy = 2; movie.add(p, mc); //mc.gridy = 3; //movie.add(acrossZT, mc); //lays out the controls JPanel controls = new JPanel(); controls.setBorder(new TitledBorder("Animation Control")); GridBagConstraints c = new GridBagConstraints(); controls.setLayout(new GridBagLayout()); c.gridx = 0; c.gridy = 0; c.anchor = GridBagConstraints.WEST; controls.add(buildToolBar(), c); c.gridx = 1; controls.add(createDirectionControls(), c); c.insets = new Insets(10, 0, 10, 0); c.gridx = 0; c.gridy = 1; controls.add(createFPSControls(), c); //lays out components setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.HORIZONTAL; add(controls, gbc); gbc.gridy = 1; gbc.fill = GridBagConstraints.BOTH; /* JSeparator sep = new JSeparator(JSeparator.HORIZONTAL); sep.setPreferredSize(new Dimension(this.getWidth(), 2)); sep.setMinimumSize(new Dimension(this.getWidth(), 2)); add(sep, gbc); */ gbc.gridy = 2; add(movie, gbc); } /** * Creates a new instance. * * @param model Reference to the model. Mustn't be <code>null</code>. */ MoviePlayerUI(MoviePlayer model) { if (model == null) throw new NullPointerException("No model."); this.model = model; charWidth = getFontMetrics(getFont()).charWidth('m'); initComponents(); setDefaults(); buildGUI(); } /** Sets the default. */ void setDefaults() { int maxZ = model.getMaxZ(); int maxT = model.getMaxT(); zSlider.setEnabled(maxZ != 0); startZ.setEnabled(maxZ != 0); endZ.setEnabled(maxZ != 0); tSlider.setEnabled(maxT != 0); startT.setEnabled(maxT != 0); endT.setEnabled(maxT != 0); acrossZ.setEnabled(maxZ != 0); acrossT.setEnabled(maxT != 0); //acrossZT.setEnabled(!((maxZ == 0) || (maxT == 0))); switch (model.getMovieIndex()) { case MoviePlayerDialog.ACROSS_Z: acrossZ.setSelected(true); break; case MoviePlayerDialog.ACROSS_T: acrossT.setSelected(true); break; case MoviePlayerDialog.ACROSS_ZT: acrossZ.setSelected(true); acrossT.setSelected(true); } } /** * Returns the movie type corresponding to the specified UI type. * * @param uiType The UI type. * @return See above. */ int getMovieType(int uiType) { switch (uiType) { case LOOP_CMD: return MoviePlayer.LOOP; case BACKWARD_CMD: return MoviePlayer.BACKWARD; case FORWARD_CMD: return MoviePlayer.FORWARD; case PINGPONG_CMD: return MoviePlayer.PINGPONG; case LOOP_BACKWARD_CMD: return MoviePlayer.LOOP_BACKWARD; } throw new IllegalArgumentException("UI index not supported."); } /** Sets the movie type to {@link #FORWARD_CMD}. */ void setDefaultMovieType() { movieTypes.setSelectedIndex(FORWARD_CMD); } /** * Sets the start z-section. * * @param v The value to set. */ void setStartZ(int v) { startZ.setText(""+(v+1)); zSlider.setStartValue(v); } /** * Sets the start timepoint. * * @param v The value to set. */ void setStartT(int v) { startT.setText(""+(v+1)); tSlider.setStartValue(v); } /** * Sets the end z-section. * * @param v The value to set. */ void setEndZ(int v) { endZ.setText(""+(v+1)); zSlider.setEndValue(v); } /** * Sets the end timepoint. * * @param v The value to set. */ void setEndT(int v) { endT.setText(""+(v+1)); tSlider.setEndValue(v); } /** * Swaps the {@link #play} and {@link #pause} depending on the * specified flag. * * @param b Pass <code>true</code> to set the {@link #pause} button * <code>false</code> otherwise. */ void setMoviePlay(boolean b) { toolBar.removeAll(); if (b) toolBar.add(pause); else toolBar.add(play); toolBar.add(stop); toolBar.repaint(); } /** * Updates the UI components displaying the timer's delay. * * @param v The value to set. */ void setTimerDelay(int v) { fps.setValue(Integer.valueOf(v)); editor.setText(""+v); } /** * Sets the movie index. * * @param index The index of the movie. */ void setMovieIndex(int index) { switch (index) { case MoviePlayerDialog.ACROSS_Z: acrossZ.setSelected(true); break; case MoviePlayerDialog.ACROSS_T: acrossT.setSelected(true); } } /** * Initiates a click on the {@link #pause} button * if the passed index is {@link MoviePlayerDialog#DO_CLICK_PAUSE} * on the {@link #play} button * if the passed index is {@link MoviePlayerDialog#DO_CLICK_PLAY}. * * @param index The index identifying the button. */ void doClick(int index) { switch (index) { case MoviePlayerDialog.DO_CLICK_PAUSE: pause.doClick(); break; case MoviePlayerDialog.DO_CLICK_PLAY: play.doClick(); break; } } }