package view;
//----- JDK Imports ------------------------------------------------------------
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DecimalFormat;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.Timer;
import javax.swing.WindowConstants;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
//----- Quicktime Imports ------------------------------------------------------
import quicktime.QTException;
import quicktime.app.view.MoviePlayer;
import quicktime.app.view.QTFactory;
import quicktime.app.view.QTJComponent;
import quicktime.qd.Pict;
import quicktime.std.StdQTConstants;
import quicktime.std.StdQTException;
import quicktime.std.clocks.ExtremesCallBack;
import quicktime.std.clocks.RateCallBack;
import quicktime.std.clocks.TimeRecord;
import quicktime.std.movies.Movie;
//----- Phoenix Imports --------------------------------------------------------
import controller.MovieController;
import controller.PhoenixController;
import util.ImageUtils;
/**
* Video Phoenix
* Version 0.2.0
* Copyright (c) 2007 Lunderskov, Ian; Pan, Jiabei; Rebelsky, Samuel;
* Whisenhunt, Heather; Young, Ian; Zuleta Benavides, Luis.
* All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This window displays a QuickTime movie and provides navigation and volume
* control.
*
* @author Lunderskov, Ian; Pan, Jiabei; Rebelsky, Samuel; Whisenhunt, Heather;
* Young, Ian; Zuleta Benavides, Luis
* @author Glimmer Labs
* @version 0.2.0
*/
@SuppressWarnings("serial")
public class MovieWindow extends JInternalFrame implements ActionListener,
ChangeListener, InternalFrameListener, KeyListener, MouseListener
{
/*--------*-------------------------------------------------------------------
* Fields *
*--------*/
// left border of the next MovieWindow (so that they are stacked)
public static int nextleft = 5;
// top border of the next MovieWindow (so that they are stacked)
public static int nexttop = 5;
// MovieController for this MovieWindow
public static MovieController controller;
// milliseconds between time updates
public final int CLOCK = 1;
// main display window
public PhoenixWindow view;
// movie screen
public MoviePlayer player;
// QuickTime Swing component
public QTJComponent qtjcomp;
// original time scale of the movie
public int timeScale;
// duration of the movie
public int duration;
// a timer to be used to call actionPerformed every CLOCK milliseconds
// to perform necessary updates
public Timer timer;
// Quicktime time between the first two interesting frames,
// assumed to be the standard time between frames
public int interval = 0;
// name of the monitor displayed in the title bar
private String name;
// whether the movie is currently playing
Boolean playing = false;
// whether a mouse button or arrow key is being pressed
Boolean pressed = false;
// the movie
Movie movie;
// create slider, buttons, and icons
public JPanel panel;
public JPanel buttonPanel;
public JSlider slider;
public VolumeWindow volume;
public JButton play;
JButton ff;
JButton rw;
JButton vol;
JButton prev;
JButton next;
ImageIcon playIcon =
new ImageIcon(PhoenixController.getImage("play.gif"));
ImageIcon pauseIcon =
new ImageIcon(PhoenixController.getImage("pause.gif"));
ImageIcon ffIcon =
new ImageIcon(PhoenixController.getImage("ff.gif"));
ImageIcon rwIcon =
new ImageIcon(PhoenixController.getImage("rw.gif"));
ImageIcon volIcon =
new ImageIcon(PhoenixController.getImage("vol.gif"));
ImageIcon prevIcon =
new ImageIcon(PhoenixController.getImage("prev-frame.gif"));
ImageIcon nextIcon =
new ImageIcon(PhoenixController.getImage("next-frame.gif"));
// 0 = Normal movie
// 1 = Empty movie
// 2 = Audio-only movie
private int movieType = 0;
/*--------------*-------------------------------------------------------------
* Constructors *
*--------------*/
/**
* Creates a new instance of Monitor
*
* @param name Name of the movie to be displayed in the monitor
* @param mainframe MainFrame to which the monitor will belong
* @throws QTException
*/
public MovieWindow(MovieController controller, String name, Movie mov)
throws Exception
{
// set resizable, closable, maximizable, iconifiable
super(name, true, true, true, true);
if (mov == null) return;
MovieWindow.controller = controller;
this.view = MovieWindow.controller.phoenix.view;
this.name = name;
this.movie = mov;
this.duration = mov.getDuration();
new rateCallBack(movie);
new endCallBack(movie);
// set movie type
if (this.duration == 0)
{
movieType = 1;
} // if (this.duration == 0)
else if ((mov.getBounds().getWidth() == 0)
|| (mov.getBounds().getHeight() == 0))
{
movieType = 2;
} // else if ((mov.getBounds().getWidth() == 0) ...
// dispose of the window when closed
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// add a frame listener to detect when window is closed/minimized
this.addInternalFrameListener(this);
// add controls only to non-empty movies
if (movieType != 1)
{
// create the volume window, to be displayed when volume control is open
this.volume = new VolumeWindow(this, 0, 100);
// create control buttons
this.panel = new JPanel();
this.play = new JButton(this.playIcon);
this.play.setMargin(new Insets(2, 2, 2, 2));
this.play.setToolTipText("Play");
this.rw = new JButton(this.rwIcon);
this.rw.setMargin(new Insets(2, 2, 2, 2));
this.rw.setToolTipText("Rewind");
this.ff = new JButton(this.ffIcon);
this.ff.setMargin(new Insets(2, 2, 2, 2));
this.ff.setToolTipText("Fast Forward");
this.play.addActionListener(this);
this.rw.addActionListener(this);
this.ff.addActionListener(this);
this.play.setFocusPainted(false);
this.ff.setFocusPainted(false);
this.rw.setFocusPainted(false);
this.vol = new JButton(this.volIcon);
this.vol.addActionListener(this);
this.vol.setToolTipText("Volume");
this.vol.setFocusPainted(false);
this.vol.setMargin(new Insets(2, 2, 2, 2));
this.prev = new JButton(this.prevIcon);
this.prev.addActionListener(this);
this.prev.setToolTipText("Previous Frame");
this.prev.setFocusPainted(false);
this.prev.setMargin(new Insets(2, 2, 2, 2));
this.next = new JButton(this.nextIcon);
this.next.addActionListener(this);
this.next.setToolTipText("Next Frame");
this.next.setFocusPainted(false);
this.next.setMargin(new Insets(2, 2, 2, 2));
this.buttonPanel = new JPanel();
this.buttonPanel.add(this.prev, BorderLayout.LINE_START);
this.buttonPanel.add(this.rw, BorderLayout.LINE_START);
this.buttonPanel.add(this.play, BorderLayout.LINE_START);
this.buttonPanel.add(this.ff, BorderLayout.LINE_START);
this.buttonPanel.add(this.next, BorderLayout.LINE_START);
this.buttonPanel.add(this.vol, BorderLayout.LINE_START);
this.buttonPanel.setVisible(true);
this.panel.setLayout(new BorderLayout());
this.panel.add(buttonPanel, BorderLayout.CENTER);
this.add(this.panel, BorderLayout.SOUTH);
this.panel.setBorder(BorderFactory.createEtchedBorder(
EtchedBorder.LOWERED));
this.panel.getRootPane().setDefaultButton(this.play);
// set interval for slider
if (movieType == 0)
{
this.interval = controller.getInterval(mov);
} // if (movieType == 0)
else
{
this.interval = 1;
} // else
// create movie player and seek bar
try
{
this.player = new MoviePlayer(movie);
this.timeScale = mov.getTimeScale();
this.slider = new JSlider(0);
this.slider.setValue(0);
this.slider.setMaximum(this.duration / this.interval);
this.slider.setVisible(true);
this.slider.addChangeListener(this);
this.slider.setToolTipText("Seek");
this.slider.addKeyListener(this);
this.slider.addMouseListener(this);
if (movieType == 0)
{
// create movie player to display the movie
this.qtjcomp = QTFactory.makeQTJComponent(this.player);
this.add(this.qtjcomp.asJComponent(), BorderLayout.CENTER);
this.slider.setPreferredSize(new Dimension(
movie.getBounds().getWidth() - 180, 20));
} // if (movieType == 0)
else
{
// display a no-image picture
Pict pict = ImageUtils.pictFromFile(PhoenixController.getImage(
"noimage.bmp"));
Image image = util.ImageUtils.makeJimage(pict, 200, 150);
ImageIcon icon = new ImageIcon(image);
JLabel label = new JLabel(icon);
this.getContentPane().add(label, BorderLayout.CENTER);
this.slider.setPreferredSize(new Dimension(80, 20));
} // else
this.panel.add(this.slider, BorderLayout.NORTH);
} // try
catch (StdQTException stdqte)
{
stdqte.printStackTrace();
} // catch (StdQTException)
catch (QTException qte)
{
qte.printStackTrace();
} // catch (QTException)
catch (Exception e)
{
e.printStackTrace();
} // catch (Exception)
// initialize timer
this.timer = new Timer(this.CLOCK, this);
this.timer.start();
} // if (movieType != 1)
else
{
// display a no-image picture
Pict pict = ImageUtils.pictFromFile(PhoenixController.getImage(
"noimage.bmp"));
Image image = util.ImageUtils.makeJimage(pict, 200, 150);
ImageIcon icon = new ImageIcon(image);
JLabel label = new JLabel(icon);
this.getContentPane().add(label, BorderLayout.SOUTH);
}
setVisible(true);
this.view.add(this);
this.view.moveToFront(this);
this.setSelected(true);
// so that multiple monitors don't open right on top of each other
MovieWindow.nextleft += 10;
MovieWindow.nexttop += 20;
if (MovieWindow.nexttop > 100)
{
MovieWindow.nexttop = 5;
} // if (nexttop > 100)
this.pack();
} // MovieWindow(MovieController, String, Movie)
/*---------*------------------------------------------------------------------
* Methods *
*---------*/
/**
* Get the MovieController for this MovieWindow
*
* @return controller MovieController for this MovieWindow
*/
public MovieController getController()
{
return MovieWindow.controller;
} // getController()
/**
* Set the bounds of the monitor window
*
* @param x X location of the window
* @param y Y location of the window
* @param width Width of the window
* @param height Height of the window
*/
public void setBounds(int x, int y, int width, int height)
{
super.setBounds(x, y, width, height);
} // setBounds(int, int, int, int)
/**
* Update the time display
*/
public void showTime()
{
try
{
Double seconds = (double) this.movie.getTime() / (double) this.timeScale;
DecimalFormat df = new DecimalFormat("0.00");
this.setTitle(this.name + " - " + df.format(seconds) + " sec");
this.slider.setValue(this.movie.getTime() / this.interval);
} // try
catch (Exception e)
{
e.printStackTrace();
} // catch (Exception)
} // showTime()
/*------------------*---------------------------------------------------------
* Listener Methods *
*------------------*/
/**
* Handle all events
*
* @param event ActionEvent in question
*/
public void actionPerformed(ActionEvent event)
{
try
{
// update the time in the title bar
if (event.getSource() == this.timer)
{
this.showTime();
} // if (event.getSource() == this.timer)
// if user selected the "play" button
else if (event.getSource() == this.play)
{
// if movie is currently playing
if (this.playing)
{
// pause the movie
this.movie.setRate(0f);
} // if (this.playing)
// if movie is finished
else if (this.movie.getTime() == this.duration)
{
// prepare movie to be replayed
this.movie.goToBeginning();
this.movie.setRate(1.0f);
} // else if (this.movie.getTime() == this.duration)
// if the movie is paused
else
{
// play the movie
this.movie.setRate(1.0f);
} // else
} // else if (event.getSource() == this.play)
// if user selected the rewind button
else if (event.getSource() == this.rw)
{
controller.rewind(this.movie);
} // else if (event.getSource() == this.rw)
// if user selected the fast-forward button
else if (event.getSource() == this.ff)
{
controller.fastForward(this.movie);
} // else if (event.getSource() == this.ff)
// if user selected the volume button
else if (event.getSource() == this.vol)
{
this.volume.volume.setValue(java.lang.Math.round(
100 * (float)(java.lang.Math.sqrt(java.lang.Math.sqrt(
(float)this.movie.getVolume() / 128)))));
this.volume.setVisible(!this.volume.isVisible());
this.volume.setLocationRelativeTo(this.vol);
this.volume.setLocation(this.volume.getX(), this.volume.getY() - 50
- this.vol.getHeight() / 2);
} // else if (event.getSource() == this.vol)
else if (event.getSource() == this.prev)
{
controller.prevFrame(this.movie);
} // else if (event.getSource() == this.prev)
else if (event.getSource() == this.next)
{
controller.nextFrame(this.movie);
} // else if (event.getSource() == this.next)
} // try
catch (Exception e)
{
e.printStackTrace();
} // catch (Exception)
} // actionPerformed(ActionEvent)
/**
* Handle internal frame closing
*
* @param event frame closing event
*/
public void internalFrameClosing(InternalFrameEvent event)
{
// if movie is not empty
if (this.movieType != 1)
{
this.timer.stop();
this.player = null;
this.timer = null;
try
{
this.movie.stop();
this.movie = null;
} // try
catch (StdQTException stdqte)
{
stdqte.printStackTrace();
} // catch (StdQTException)
} // if (this.movieType != 1)
} // internalFrameClosing(InternalFrameEvent)
/**
* Handle internal frame closed
*
* @param event frame closed event
*/
public void internalFrameClosed(InternalFrameEvent event)
{
// do nothing when frame is closed
} // internalFrameClosed(InternalFrameEvent)
/**
* Handle internal frame opened
*
* @param event frame opened event
*/
public void internalFrameOpened(InternalFrameEvent event)
{
// do nothing when frame is opened
} // internalFrameOpened(InternalFrameEvent)
/**
* Handle internal frame minimization
*
* @param event frame minimization event
*/
public void internalFrameIconified(InternalFrameEvent event)
{
// do nothing when frame is minimized
} // internalFrameIconified(InternalFrameEvent)
/**
* Handle internal frame expanded
*
* @param event frame expanded event
*/
public void internalFrameDeiconified(InternalFrameEvent event)
{
// do nothing when frame is expanded
} // internalFrameDeiconified(InternalFrameEvent)
/**
* Handle internal frame activated
*
* @param event frame activated event
*/
public void internalFrameActivated(InternalFrameEvent event)
{
if (this.movieType != 1)
{
this.panel.getRootPane().setDefaultButton(this.play);
} // if (this.movieType != 1)
} // internalFrameActivated(InternalFrameEvent)
/**
* Handle internal frame deactivated
*
* @param event frame deactivated event
*/
public void internalFrameDeactivated(InternalFrameEvent event)
{
// do nothing when frame is deactivated
} // internalFrameDeactivated(InternalFrameEvent)
/**
* Handle state changes in the seek bar
*
* @param event slider state changed event
*/
public void stateChanged(ChangeEvent event)
{
// a true indicates that the change on the slider is caused by mouse/
// keyboard instead of timer.
if (this.pressed)
{
JSlider source = (JSlider) event.getSource();
// set the time in the movie to the new time indicated by the slider
try
{
this.movie.setTime(new TimeRecord(this.movie.getTimeScale(),
source.getValue() * this.interval));
} // try
catch (Exception e)
{
e.printStackTrace();
} // catch (Exception)
} // if (this.pressed)
} // stateChanged(ChangeEvent)
/**
* Handle mouse clicks
*
* @param event mouse clicked event
*/
public void mouseClicked(MouseEvent event)
{
// do nothing when mouse is clicked
} // mouseClicked(MouseEvent)
/**
* Handle mouse entering MovieWindow
*
* @param event mouse entered event
*/
public void mouseEntered(MouseEvent event)
{
// do nothing when mouse enters MovieWindow
} // mouseEntered(MouseEvent)
/**
* Handle mouse exiting MovieWindow
*
* @param event mouse exited event
*/
public void mouseExited(MouseEvent event)
{
// do nothing when mouse exits MovieWindow
} // mouseExited(MouseEvent)
/**
* Handle mouse presses
*
* @param event mouse pressed event
*/
public void mousePressed(MouseEvent event)
{
this.pressed = true;
} // mousePressed(MouseEvent)
/**
* Handle mouse releases
*
* @param event mouse released event
*/
public void mouseReleased(MouseEvent event)
{
this.pressed = false;
} // mouseReleased(MouseEvent)
/**
* Handle key presses
*
* @param event key pressed event
*/
public void keyPressed(KeyEvent event)
{
if ((event.getKeyCode() == KeyEvent.VK_LEFT)
|| (event.getKeyCode() == KeyEvent.VK_DOWN)
|| (event.getKeyCode() == KeyEvent.VK_RIGHT)
|| (event.getKeyCode() == KeyEvent.VK_UP))
{
this.pressed = true;
} // if ((event.getKeyCode() == KeyEvent.VK_LEFT) ...
} // keyPressed(KeyEvent)
/**
* Handle key releases
*
* @param event key released event
*/
public void keyReleased(KeyEvent event)
{
if ((event.getKeyCode() == KeyEvent.VK_LEFT)
|| (event.getKeyCode() == KeyEvent.VK_DOWN)
|| (event.getKeyCode() == KeyEvent.VK_RIGHT)
|| (event.getKeyCode() == KeyEvent.VK_UP))
{
this.pressed = false;
} // if ((event.getKeyCode() == KeyEvent.VK_LEFT) ...
} // keyReleased(KeyEvent)
/**
* Handle key types
*
* @param event key typed event
*/
public void keyTyped(KeyEvent event)
{
// do nothing when key is typed
// handled in keyPressed and keyReleased
} // keyTyped(KeyEvent)
class rateCallBack extends RateCallBack
{
public rateCallBack(Movie mov)
throws Exception
{
super(mov.getTimeBase(), 0, StdQTConstants.triggerRateChange);
callMeWhen();
} // rateCallBack(Movie)
public void execute()
{
if (movie == null) return;
try
{
if (rateWhenCalled > 0)
{
if (movie.getTime() == movie.getDuration())
{
movie.goToBeginning();
} // if (movie.getTime() == movie.getDuration())
playing = true;
play.setIcon(pauseIcon);
play.setToolTipText("Pause");
} // (rateWhenCalled > 0)
else if (rateWhenCalled == 0)
{
playing = false;
play.setIcon(playIcon);
play.setToolTipText("Play");
} // else if (rateWhenCalled == 0)
callMeWhen();
} // try
catch (Exception e)
{
e.printStackTrace();
} // catch (Exception)
} // execute()
} // class RateCallBack
class endCallBack extends ExtremesCallBack
{
private Movie mov;
public endCallBack(Movie movie)
throws StdQTException, QTException
{
super(movie.getTimeBase(), StdQTConstants.triggerAtStop);
mov = movie;
callMeWhen();
} // endCallBack(Movie)
public void execute()
{
if (mov == null) return;
try
{
mov.setRate(0f);
callMeWhen();
} // try
catch (StdQTException stdqte)
{
stdqte.printStackTrace();
} // catch (StdQTException)
} // execute()
} // class endCallBack
} // class MovieWindow