/*
This file is part of JFLICKS.
JFLICKS 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 3 of the License, or
(at your option) any later version.
JFLICKS 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 JFLICKS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jflicks.ui.view.fe.screen;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import org.jflicks.imagecache.ImageCache;
import org.jflicks.imagecache.ImageCacheProperty;
import org.jflicks.mvc.View;
import org.jflicks.player.Player;
import org.jflicks.rc.RC;
import org.jflicks.rc.RCProperty;
import org.jflicks.tv.Recording;
import org.jflicks.ui.view.fe.BaseCustomizePanel;
import org.jflicks.util.RuntimeId;
import org.jdesktop.swingx.painter.ImagePainter;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.osgi.util.tracker.ServiceTracker;
/**
* This class is a basic screen class that others extend.
*
* @author Doug Barnum
* @version 1.0
*/
public abstract class Screen extends BaseCustomizePanel implements RCProperty,
EventHandler, ImageCacheProperty, Screenable, Comparable<Screen> {
/**
* The Screen needs a title property.
*/
public static final String TITLE_PROPERTY = "Screen-Title";
private ArrayList<ScreenListener> screenList =
new ArrayList<ScreenListener>();
private String title;
private boolean blocking;
private boolean done;
private ServiceTracker playerServiceTracker;
private RC rc;
private ImageCache imageCache;
private BufferedImage defaultBackgroundImage;
private BufferedImage currentBackgroundImage;
private View view;
private long lastCommandMillis;
/**
* This base class does the necessary OSGi Event code so we insist
* extensions implement this method so the Event command can be passed
* along.
*
* @param command A command from the RC service.
*/
public abstract void commandReceived(String command);
/**
* A Screen might need to persist it's "state" so when users revisit
* they will find things the way they left them. If it is not important
* to do so a Screen can chose not to do anything.
*/
public abstract void save();
/**
* Simple empty constructor.
*/
public Screen() {
setFocusable(true);
setDone(true);
requestFocus();
EscapeAction escapeAction = new EscapeAction();
InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
map.put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
getActionMap().put("escape", escapeAction);
}
/**
* Each screen gets access to the View class running the whole show.
*
* @return A View instance.
*/
public View getView() {
return (view);
}
/**
* Each screen gets access to the View class running the whole show.
*
* @param v A View instance.
*/
public void setView(View v) {
view = v;
}
/**
* Convenience method for extensions so external images can be used
* instead of the embedded jar images for their backgrounds. We look
* in the theme directory for a file with the given name.
*
* @param name File to look for at least as a resource.
* @return A buffered image.
*/
public BufferedImage getImageByName(String name) {
BufferedImage result = null;
if (name != null) {
name = name + ".png";
try {
// First see if it's in a theme directory...
File theme = getCurrentThemeDirectory();
File file = new File(theme, name);
if (file.exists()) {
result = ImageIO.read(file);
} else {
result = ImageIO.read(getClass().getResource(name));
}
} catch (IOException ex) {
result = null;
}
}
return (result);
}
/**
* {@inheritDoc}
*/
public void restartUI() {
String t = getTitle();
if (t != null) {
t = t.replaceAll(" ", "_");
setDefaultBackgroundImage(getImageByName(t));
}
super.restartUI();
}
/**
* {@inheritDoc}
*/
public void performControl() {
}
/**
* The title of the screen. This title will show up on the main window
* as a button.
*
* @return The title as a String.
*/
public String getTitle() {
return (title);
}
/**
* The title of the screen. This title will show up on the main window
* as a button.
*
* @param s The title as a String.
*/
public void setTitle(String s) {
title = s;
}
/**
* Property that signifies the user is done with this Screen.
*
* @return True if done.
*/
public boolean isDone() {
return (done);
}
/**
* Property that signifies the user is done with this Screen.
*
* @param b True if done.
*/
public void setDone(boolean b) {
boolean old = done;
done = b;
firePropertyChange("Done", old, done);
}
public boolean isBlocking() {
return (blocking);
}
public void setBlocking(boolean b) {
boolean old = blocking;
blocking = b;
firePropertyChange("Blocking", old, blocking);
}
/**
* A screen has a default background image.
*
* @return The default background image as a BufferedImage instance.
*/
public BufferedImage getDefaultBackgroundImage() {
return (defaultBackgroundImage);
}
/**
* A screen has a default background image.
*
* @param bi The default background image as a BufferedImage instance.
*/
public void setDefaultBackgroundImage(BufferedImage bi) {
defaultBackgroundImage = bi;
if (bi != null) {
ImagePainter p = (ImagePainter) getBackgroundPainter();
if (p != null) {
p.setImage(bi);
repaint();
} else {
p = new ImagePainter(bi);
p.setScaleToFit(true);
setBackgroundPainter(p);
}
}
}
/**
* Ability to change the background image of a screen when ever it
* is necessary.
*
* @return A given BufferedImage instance.
*/
public BufferedImage getCurrentBackgroundImage() {
return (currentBackgroundImage);
}
/**
* Ability to change the background image of a screen when ever it
* is necessary.
*
* @param bi A given BufferedImage instance.
*/
public void setCurrentBackgroundImage(BufferedImage bi) {
currentBackgroundImage = bi;
if (bi != null) {
ImagePainter p = (ImagePainter) getBackgroundPainter();
if (p != null) {
p.setImage(bi);
repaint();
} else {
p = new ImagePainter(bi);
p.setScaleToFit(true);
setBackgroundPainter(p);
}
}
}
/**
* A Screen is instantiated with a ServiceTracker which can find a
* Player for a particular screen. This property is populated by a
* Theme implementation as they have the knowledge of the type of
* player their screens need to have to do their job.
*
* @return A ServiceTracker instance.
*/
public ServiceTracker getPlayerServiceTracker() {
return (playerServiceTracker);
}
/**
* A Screen is instantiated with a ServiceTracker which can find a
* Player for a particular screen. This property is populated by a
* Theme implementation as they have the knowledge of the type of
* player their screens need to have to do their job.
*
* @param t A ServiceTracker instance.
*/
public void setPlayerServiceTracker(ServiceTracker t) {
playerServiceTracker = t;
}
/**
* Each screen could have a Player (or Players) that they need to play
* media for the user. This method will get the "first" service reference
* found. Most often a Screen will have just one and this method will
* return it. If there are more than one use the other getPlayer method
* to get it by it's type.
*
* @return A Player instance if it exists.
*/
public Player getPlayer() {
Player result = null;
ServiceTracker st = getPlayerServiceTracker();
if (st != null) {
result = (Player) st.getService();
}
return (result);
}
/**
* A Screen can have more than one Player and any one
* particular one can be acquired by supplying the type.
* Of course this makes it limited to one each kind of
* type but we do not believe this is limiting.
*
* @param type The type of Player we want.
* @return A Player instance if it exists.
*/
public Player getPlayer(String type) {
Player result = null;
ServiceTracker st = getPlayerServiceTracker();
if ((st != null) && (type != null)) {
Object[] array = st.getServices();
if ((array != null) && (array.length > 0)) {
for (int i = 0; i < array.length; i++) {
Player p = (Player) array[i];
if (type.equals(p.getType())) {
result = p;
break;
}
}
}
}
return (result);
}
public Player[] getPlayers() {
Player[] result = null;
ServiceTracker st = getPlayerServiceTracker();
if (st != null) {
Object[] array = st.getServices();
if ((array != null) && (array.length > 0)) {
result = new Player[array.length];
for (int i = 0; i < array.length; i++) {
result[i] = (Player) array[i];
}
}
}
return (result);
}
/**
* The Activator for the FrontEndView keeps us uptodate with the
* current RC instance. We don't expect more than one running or
* needed so we just keep track of one.
*
* @return A given RC instance.
*/
public RC getRC() {
return (rc);
}
/**
* The Activator for the FrontEndView keeps us uptodate with the
* current RC instance. We don't expect more than one running or
* needed so we just keep track of one.
*
* @param r A given RC instance.
*/
public void setRC(RC r) {
rc = r;
}
/**
* {@inheritDoc}
*/
public ImageCache getImageCache() {
return (imageCache);
}
/**
* {@inheritDoc}
*/
public void setImageCache(ImageCache ic) {
imageCache = ic;
}
private long getLastCommandMillis() {
return (lastCommandMillis);
}
private void setLastCommandMillis(long l) {
lastCommandMillis = l;
}
public long getLastActivityMillis() {
long result = getLastCommandMillis();
Player[] all = getPlayers();
if ((all != null) && (all.length > 0)) {
for (int i = 0; i < all.length; i++) {
if (all[i].isPlaying()) {
result = System.currentTimeMillis();
break;
}
}
}
return (result);
}
/**
* {@inheritDoc}
*/
public void addScreenListener(ScreenListener l) {
screenList.add(l);
}
/**
* {@inheritDoc}
*/
public void removeScreenListener(ScreenListener l) {
screenList.remove(l);
}
/**
* Convenience method to fire an event with a certain type.
*
* @param type A given type.
*/
public void fireScreenEvent(int type) {
processScreenEvent(new ScreenEvent(this, type));
}
/**
* Convenience method to fire an event with a certain type and Recording.
*
* @param type A given type.
* @param r A given Recording.
*/
public void fireScreenEvent(int type, Recording r) {
processScreenEvent(new ScreenEvent(this, type, r));
}
protected synchronized void processScreenEvent(ScreenEvent event) {
synchronized (screenList) {
for (int i = 0; i < screenList.size(); i++) {
ScreenListener l = screenList.get(i);
l.screenUpdate(event);
}
}
}
/**
* The OSGi Event Handler method we need to implement to receive
* messages from the RC service (or any other event from OSGi.
*
* @param event A given Event instance.
*/
public void handleEvent(Event event) {
String command = (String) event.getProperty("command");
String runtimeid = (String) event.getProperty("runtimeid");
if ((command != null) && (runtimeid != null) && (!isDone())) {
if (runtimeid.equals(RuntimeId.getInstance().getId())) {
commandReceived(command);
setLastCommandMillis(System.currentTimeMillis());
}
}
}
/**
* The standard hashcode override.
*
* @return An int value.
*/
public int hashCode() {
return (getTitle().hashCode());
}
/**
* The equals override method.
*
* @param o A gven object to check.
* @return True if the objects are equal.
*/
public boolean equals(Object o) {
boolean result = false;
if (o == this) {
result = true;
} else if (!(o instanceof Screen)) {
result = false;
} else {
Screen scr = (Screen) o;
String s = getTitle();
if (s != null) {
result = s.equals(scr.getTitle());
}
}
return (result);
}
/**
* The comparable interface.
*
* @param s The given Screen instance to compare.
* @throws ClassCastException on the input argument.
* @return An int representing their "equality".
*/
public int compareTo(Screen s) throws ClassCastException {
int result = 0;
if (s == null) {
throw new NullPointerException();
}
if (s == this) {
result = 0;
} else {
String title0 = getTitle();
String title1 = s.getTitle();
if ((title0 != null) && (title1 != null)) {
result = title0.compareTo(title1);
}
}
return (result);
}
class EscapeAction extends AbstractAction {
public EscapeAction() {
}
public void actionPerformed(ActionEvent e) {
if (!isBlocking()) {
save();
setDone(true);
}
}
}
}