/* * Copyright 2008 Eckhart Arnold (eckhart_arnold@hotmail.com). * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package de.eckhartarnold.client; import java.util.ArrayList; import java.util.HashMap; import com.google.gwt.user.client.History; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Widget; // TODO: Configurable duration, either in info.json file (done!) or even better by the user (to do!) /** * Plays a slide show on an {@link ImagePanel}. * * <p>The slide show can be stepped through * via the <code>show()</code> and <code>next()</code> methods or * run automatically via the <code>start()</code> methods. * * <p>Class <code>Slideshow</code> "controls" the slide show, i.e. * if a <code>Slideshow</code> object is instantiated, displaying an image * should always be done through a call its <code>shod()</code>-method * an never by calling the <code>showImage()</code> methods of the image * panel directly. Instances of class slide show also register listeners * that "listen" to slide show events like starting, stopping, fading and * showing of a particular slide. * * @author eckhart */ public class Slideshow implements AttachmentListener { // public static String[] urlList(String txt) { // // } static final String SLIDE_TOKEN = "Slide_"; protected class ImageDisplayListener implements ImagePanel.DisplayListener { public void onDisplay() { if (isRunning()) { if (current == terminal && !loop) stop(); else timer.schedule(imagePanel.getDuration()); } fireShow(current); } public void onFade() { fireFade(); } } protected class SlideshowTimer extends Timer { public void run() { next(); } } ImagePanel imagePanel; // package private so that it can be manipulated by the control panel private String[] directories; private String[] slides; private HashMap<String, int[][]> sizes; private int current = -1; private int terminal; private int firedShowNr = -1; private boolean loop = false, running = false; private Timer timer = new SlideshowTimer(); private ImageDisplayListener loadListener = new ImageDisplayListener(); private ArrayList<SlideshowListener> listenerList = new ArrayList<SlideshowListener>(); /** * Constructor for class <code>Slideshow</code>. Takes a * {@link ImagePanel}, where the slide show is to be displayed and * a list of image file names as argument. * * @param imagePanel the panel where the slides will be displayed * @param urlList a list of URLs pointing to the images to be displayed */ public Slideshow(ImagePanel imagePanel, String[] urlList) { assert urlList.length > 0; this.imagePanel = imagePanel; this.imagePanel.addAttachmentListener(this); slides = urlList; directories = null; sizes = null; } /** * Constructor for class <code>Slideshow</code>. Takes a * {@link ImagePanel}, where the slide show is to be displayed and * a list of image names, directory names, where different directories are * to contain differently sized versions of the same image and a dictionary * that contains information on the image sizes of the different version of * each image. * * The images listed in the <code>names</code> parameter must be contained * in each of the directories in a different size, i.e. if the directories * are "http://.../640x480" and "http://.../800x600", a version of the same * image with a different size should be contained in each of these * directories. The images in one directory do not need to be all of the * same size, but they may vary in size and aspect ratio. The exact sizes * must be contained in the <code>sizes</code> tuple, which contains for * each directory a map that associates to each image name the exact size of * the image version in the corresponding directory. * * @param imagePanel the panel where the slides will be displayed * @param names a list of image names. Only the base name should be * given, not the full URL, i.e. "image.jpg" * @param directories the URLs of one or more directories corresponding to * different resolutions. * @param sizes a dictionary that assigns every image name a list * of size tuples. */ public Slideshow(ImagePanel imagePanel, String[] names, String[] directories, HashMap<String, int[][]> sizes) { assert names.length > 0; assert sizes.size() == names.length : "names: "+names.length+", but sizes: "+sizes.size(); this.imagePanel = imagePanel; this.imagePanel.addAttachmentListener(this); slides = names; this.directories = directories; this.sizes = sizes; } /** * Adds a <code>SlideshowListener</code> to the listener list. * * @param listener the <code>SlideshowListener</code> to be added */ public void addSlideshowListener(SlideshowListener listener) { listenerList.add(listener); } /** * Show the previous slide. If the slide show was already at the beginning * then the last slide if picked again. */ public void back() { int sequel = current-1; if (sequel < 0) sequel = slides.length-1; show(sequel); } /** * Returns the number of the currently displayed slide. In case the slide * show has not yet started -1 is returned * * @return number of the currently displayed slide */ public int getCurrentSlide() { return current; } /** * Returns the time delay in milliseconds between the display of two * slides. * @return time delay in milliseconds. */ public int getDuration() { return imagePanel.getDuration(); } /** * Returns the image panel that is used by the slide show. * @return the image panel of the slide show */ public ImagePanel getImagePanel() { return imagePanel; } /** * Returns true, if the slide show is running. * @return true, if slide show is running, false otherwise */ public boolean isRunning() { return running; } /** * Show the next slide. If the last slide of the slide show was shown * before, then the first slide is displayed again. */ public void next() { int sequel = current+1; if (sequel >= size()) sequel = 0; show(sequel); } public void onLoad(Widget sender) { } public void onUnload(Widget sender) { stop(); } /** * Removes a <code>SlideshowListener</code> from the listener list. * * @param listener the <code>SlideshowListener</code> to be removed */ public void removeSlideshowListener(SlideshowListener listener) { listenerList.remove(listener); } /** * Returns the number of slides. * * @return the number of slides */ public int size() { return slides.length; } /** * Sets the duration how long each slide is shown during the slide show. * @param duration the duration in milliseconds */ public void setDuration(int duration) { imagePanel.setDuration(duration); } /** * Enables or disables infinite loops. If enabled, the slide show will not * stop automatically after the last slide has been shown. The default value * is false. * @param enable true, if "infinite" repetition of the slide show shall be * enabled */ public void setLoop(boolean enable) { loop = enable; } /** * Shows a specific slide from the list. * * @param slideNr the number of the slide that is shown */ public void show(int slideNr) { assert slideNr < slides.length && slideNr >= -1: "Slide index out of bounds!"; if (slideNr == current) return; current = slideNr; if (current == -1) { imagePanel.clear(); return; } if (directories == null) { imagePanel.showImage(slides[current], loadListener); if (current < size()-1) Image.prefetch(slides[current+1]); } else { String[] urls = new String[directories.length]; int[][] sizes = new int[urls.length][]; for (int i = 0; i < urls.length; i++) { urls[i] = directories[i] + "/" + slides[current]; sizes[i] = this.sizes.get(slides[current])[i]; } imagePanel.showImage(urls, sizes, loadListener); if (current < size()-1) { String dir = directories[imagePanel.getSizeStep()]; Image.prefetch(dir + "/" + slides[current+1]); } } History.newItem(SLIDE_TOKEN+(current+1), false); } /** * Shows a specific slide without fading in. * * @param slideNr the number of the slide that is to be shown. */ public void showImmediately(int slideNr) { int fading = imagePanel.getFading(); imagePanel.setFading(0); show(slideNr); imagePanel.setFading(fading); } /** * Starts an automatic slide show. The slide show runs from the currently * displayed slide (or from the first slide if no slide is displayed * currently) to the first slide before the current slide (one round trip!). * The delay between the slides is the default value of 5000 ms or, * if the <code>start</code> method has been called before with a duration * value, the duration the start method was called with last time. */ // TODO: if started from gallery, slideshow should return to the gallery when finished public void start() { stop(); running = true; if (current < 0) { terminal = size() - 1; next(); } else { terminal = current-1; if (terminal < 0) terminal = size()-1; timer.schedule(imagePanel.getDuration()); } fireStart(); } // /** // * Starts an automatic slide show that shows all slides beginning with // * the currently displayed slide (or with the first slide, if no slide // * is displayed currently). // * // * @param duration the delay in milliseconds before displaying the // * following slide // */ // public void start(int duration) { // this.duration = duration; // start(); // } /** * Stops a currently running slide show. If the slide show was not running * nothing happens. */ public void stop() { if (isRunning()) { timer.cancel(); running = false; fireStop(); } } private void fireFade() { firedShowNr = -1; for (SlideshowListener listener: listenerList) listener.onFade(); } private void fireStart() { for (SlideshowListener listener: listenerList) listener.onStart(); } private void fireStop() { for (SlideshowListener listener: listenerList) listener.onStop(); } private void fireShow(int slideNr) { if (slideNr != firedShowNr) { firedShowNr = slideNr; for (SlideshowListener listener: listenerList) listener.onShow(slideNr); } } }