package org.greenstone.gatherer.feedback;
import java.awt.image.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.event.*;
import java.io.*;
import java.util.ResourceBundle;
import java.text.MessageFormat;
import javax.swing.text.NumberFormatter;
import java.text.NumberFormat;
import java.beans.*;
import java.util.ArrayList;
/**
* This is the class to generate a animation type from a list of ImageIcon.
* It will display a JDialog window and then user can specify the frames
* per second rate that the user want to use to view the list of the ImageIcon.
* @author Veronica Liesaputra
*/
public class Movie extends JDialog
implements ActionListener,ChangeListener,PropertyChangeListener
{
/**
* This variable will hold the resource of the words that is stored in Messages.properties file.
* The calling using messages.getString(someString) will caused someString to be translated
* into some other string that is hold in that file.usually it will caused it to be translated
* to the language that the user use or choose to have as stated in Locale.
*/
private static ResourceBundle messages;
/**
* This is the label that we use to display the ImageIcon.
*/
private JLabel picture;
/**
* This is a textField where user can set how many frames per second they want to view the
* ImageIcon.If user enter value that is bigger than the FPS_MAX then it will beep and user
* will need to enter new valid value or it will stay as the previous valid value.
* If user enter 0 then it will stop the animation.
*/
private JFormattedTextField textField;
/**
* This is a slider where user can set how many frames per second they want to view the
* ImageIcon.If user slide to 0 then animation stop.
*/
private JSlider framesPerSecond;
/**
* This is the minimum frame per second value.
*/
private int FPS_MIN = 0;
/**
* This is the maximum frame per second value. The value is the twice the amount of
* ImageIcon in the list.
*/
private int FPS_MAX;
/**
* This is the initial frame per second value to view the ImageIcon.
*/
private int FPS_INIT = 1;
/**
* This is the ImageIcon index that are currently showing in the label.
*/
private int frameNumber;
/**
* This is the total number of ImageIcon in the list.
*/
private int NUM_FRAMES;
/**
* This hold the list of the ImageIcon that are to be displayed.
*/
private ArrayList images;
/**
* This is telling how long is the delay for each frame to be refreshed.
*/
private int delay;
/**
* This is the timer that generate the animation of the list of ImageIcon.
* The animation will be paused twice per cycle by restarting the timer.
* If the the prame per second chosen by the user is 0 then the timer
* will be stop.
*/
private Timer timer;
/**
* This the flag to say when the animation should stop.
* If frozen = true then the animation should stop otherwise the animation
* should keep running.
*/
private boolean frozen = false;
/**
* This will sho a modal-dialog and set up the value for the animation to run properly
* according to the list of ImageIcon passed in.
* @param msg hold the resource of the words that is stored in Messages.properties file.
* @param img hold the list of ImageIcon to be displayed.
*/
public Movie (ResourceBundle msg,ArrayList img)
{
super();
images = img;
NUM_FRAMES = img.size();
FPS_MAX = img.size() * 2;
messages = msg;
setTitle(messages.getString("GenerateMovie"));
setModal(true);
setBackground(new Color (176,208,176));
}
/**
* This method will seeting up the content pane of the dialog window.
* It also setting up the timer and the delay.
* @return the content pane of the window.
*/
public JPanel create_UI()
{
JPanel cont;
cont = new JPanel();
cont.setOpaque(true);
cont.setBackground(new Color (176,208,176));
JButton cls;
cls = new JButton(messages.getString("Close"));
cls.addActionListener(new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
dispose();
if (timer != null)
timer.stop();
}
});
cls.setDefaultCapable(true);
cls.setActionCommand(messages.getString("Close"));
cls.setBackground(new Color(176,208,176));
if (images.size() <= 0)
{
JLabel empty_lbl;
empty_lbl = new JLabel ("No Image to be displayed ");
cont.add(empty_lbl);
cont.add(cls);
return cont;
}
cont.setLayout(new BoxLayout(cont, BoxLayout.PAGE_AXIS));
delay = 1000 / FPS_INIT;
JLabel sliderLabel;
sliderLabel = new JLabel(messages.getString("FramesPerSecond") + ": ", JLabel.CENTER);
sliderLabel.setBackground(new Color(176,208,176));
sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
NumberFormat numberFormat;
numberFormat = NumberFormat.getIntegerInstance();
NumberFormatter formatter;
formatter = new NumberFormatter(numberFormat);
formatter.setMinimum(new Integer(FPS_MIN));
formatter.setMaximum(new Integer(FPS_MAX));
textField = new JFormattedTextField(formatter);
textField.setBackground(new Color(224,240,224));
textField.setValue(new Integer(FPS_INIT));
textField.setColumns(5);
textField.addPropertyChangeListener(this);
textField.getInputMap().put(KeyStroke.getKeyStroke(
KeyEvent.VK_ENTER, 0),
"check");
textField.getActionMap().put("check", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if (!textField.isEditValid())
{
Toolkit.getDefaultToolkit().beep();
textField.selectAll();
}
else
{
try
{
textField.commitEdit();
}
catch (java.text.ParseException exc) { }
}
}
});
framesPerSecond = new JSlider(JSlider.HORIZONTAL,
FPS_MIN, FPS_MAX, FPS_INIT);
framesPerSecond.addChangeListener(this);
framesPerSecond.setBackground(new Color(176,208,176));
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setMinorTickSpacing(1);
framesPerSecond.setPaintTicks(true);
framesPerSecond.setPaintLabels(true);
framesPerSecond.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
picture = new JLabel();
Dimension dim;
int iw,ih;
dim = Toolkit.getDefaultToolkit().getScreenSize();
iw = (int) (dim.width - 30);
ih = (int) (dim.height * 0.75);
picture.setPreferredSize(new Dimension(iw,ih));
picture.setHorizontalAlignment(JLabel.CENTER);
picture.setAlignmentX(Component.CENTER_ALIGNMENT);
picture.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLoweredBevelBorder(),
BorderFactory.createEmptyBorder(10,10,10,10)));
picture.setBackground(new Color(176,208,176));
updatePicture(0);
JPanel labelAndTextField;
labelAndTextField = new JPanel();
labelAndTextField.add(sliderLabel);
labelAndTextField.add(textField);
labelAndTextField.add(cls);
labelAndTextField.setBackground(new Color(176,208,176));
cont.add(picture);
cont.add(framesPerSecond);
cont.add(labelAndTextField);
cont.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
timer = new Timer(delay, this);
timer.setInitialDelay(delay * 7);
timer.setCoalesce(true);
timer.start();
return cont;
}
/**
* This method will tell what should happen when the slider's arrow
* change its position.
*
If user adjusting the frame per second value using the slider
* then it will set the Textfield to have the value that slider point
* to at the moment.If the value is 0 then it will stop the animation
* otherwise it will keep animate with the new frame per second value.
* @param e is the slider when the slider's arrow change its position.
*/
public void stateChanged(ChangeEvent e)
{
JSlider source;
source = (JSlider)e.getSource();
int fps;
fps = (int)source.getValue();
if (!source.getValueIsAdjusting())
{
textField.setValue(new Integer(fps));
if (fps == 0)
{
if (!frozen)
stopAnimation();
}
else
{
delay = 1000 / fps;
timer.setDelay(delay);
timer.setInitialDelay(delay * 10);
if (frozen)
startAnimation();
}
}
else
{
textField.setText(String.valueOf(fps));
}
}
/**
* This method will set the slider value when the user adjusting
* the frame per second value using the textfield.
*
If user adjusting the frame per second value using text field, the
* slider's arrow position will be changed into that value.
* @param e the text field
*/
public void propertyChange(PropertyChangeEvent e)
{
if ("value".equals(e.getPropertyName()))
{
Number value;
value = (Number)e.getNewValue();
if (framesPerSecond != null && value != null)
{
framesPerSecond.setValue(value.intValue());
}
}
}
/**
* This method will start the timer.
*/
public void startAnimation()
{
timer.start();
frozen = false;
}
/**
* This method will stop the timer.
*/
public void stopAnimation()
{
timer.stop();
frozen = true;
}
/**
* This method will tell what should happen when the timer is start.
* When the timer start it will increase the framenumber and will
* update the label with the new image icon.
* If the timer already display all the image icon in the list, it
* will restart the timer and reanimate the image icon again.
* @param e fires when the timer start.
*/
public void actionPerformed(ActionEvent e)
{
if (frameNumber == (NUM_FRAMES - 1))
frameNumber = 0;
else
frameNumber++;
updatePicture(frameNumber);
if ((frameNumber == (NUM_FRAMES - 1)) ||
(frameNumber==(NUM_FRAMES/2 - 1)))
timer.restart();
}
/**
* This will update the label with the new ImageIcon.
* If the ImageIcon is null then the label will display an error message
* instead of the picture.
* @param frameNum this is the index of the ImageIcon to be displayed.
*/
protected void updatePicture(int frameNum)
{
if (images.get(frameNumber) != null)
{
picture.setIcon((ImageIcon) images.get(frameNumber));
}
else
{
picture.setText(messages.getString("image")+" #"
+ frameNumber + " " +
messages.getString("notfound"));
}
}
}