001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.content.slideshow;
031
032import java.awt.BorderLayout;
033import java.awt.Color;
034import java.awt.Component;
035import java.awt.Dimension;
036import java.awt.Graphics;
037import java.awt.Graphics2D;
038import java.awt.GridBagConstraints;
039import java.awt.GridBagLayout;
040import java.awt.event.KeyEvent;
041import java.awt.event.KeyListener;
042import java.awt.event.MouseAdapter;
043import java.awt.event.MouseEvent;
044import java.awt.image.BufferedImage;
045import java.io.IOException;
046import java.util.List;
047
048import javax.swing.JPanel;
049import javax.swing.JScrollPane;
050import javax.swing.RootPaneContainer;
051import javax.swing.UIManager;
052
053/**
054 * Implementation of a slideshow made up of {@link Slide}s. Binds the left and
055 * right arrow keys to forward/backward, 'q' to quit and 'f' to toggle
056 * fullscreen mode. If the current slide being displayed is also a
057 * {@link KeyListener} then keypresses other than these will be passed to the
058 * slide.
059 * 
060 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
061 * 
062 */
063public abstract class Slideshow implements KeyListener {
064        protected RootPaneContainer container;
065
066        protected List<Slide> slides;
067        protected int currentSlideIndex = -1;
068        protected Component currentSlideComp;
069
070        protected int slideWidth;
071        protected int slideHeight;
072
073        protected Slide currentSlide;
074
075        private JPanel contentPanel;
076
077        /**
078         * Default constructor.
079         * 
080         * @param container
081         *            the root window
082         * @param slides
083         *            the slides
084         * @param slideWidth
085         *            the width to display the slides
086         * @param slideHeight
087         *            the height to display the slides
088         * @param background
089         *            a background image to display behind the slides (the slides
090         *            need to be transparent!)
091         * @throws IOException
092         *             if the first slide can't be loaded
093         */
094        public Slideshow(RootPaneContainer container, List<Slide> slides, final int slideWidth, final int slideHeight,
095                        BufferedImage background) throws IOException
096        {
097                this.container = container;
098
099                this.slideWidth = slideWidth;
100                this.slideHeight = slideHeight;
101
102                final BufferedImage bg;
103                if (background == null) {
104                        bg = new BufferedImage(slideWidth, slideHeight, BufferedImage.TYPE_3BYTE_BGR);
105                        final Graphics2D g = bg.createGraphics();
106                        g.setColor(UIManager.getColor("Panel.background"));
107                        g.fillRect(0, 0, bg.getWidth(), bg.getHeight());
108                } else {
109                        bg = background;
110                }
111
112                contentPanel = new JPanel() {
113                        private static final long serialVersionUID = 1L;
114
115                        @Override
116                        public void paintComponent(Graphics g)
117                        {
118                                super.paintComponent(g);
119                                g.drawImage(bg, 0, 0, slideWidth, slideHeight, null);
120                        };
121                };
122                contentPanel.setOpaque(false);
123                contentPanel.setSize(slideWidth, slideHeight);
124                contentPanel.setPreferredSize(new Dimension(slideWidth, slideHeight));
125                contentPanel.setLayout(new GridBagLayout());
126
127                final JPanel scrollContent = new JPanel();
128                scrollContent.setLayout(new GridBagLayout());
129                scrollContent.setSize(contentPanel.getSize());
130                scrollContent.setPreferredSize(contentPanel.getSize());
131                scrollContent.add(contentPanel);
132                scrollContent.setBackground(Color.BLACK);
133
134                container.getContentPane().setBackground(Color.BLACK);
135
136                final JScrollPane scroller = new JScrollPane(scrollContent);
137                scroller.setBackground(Color.BLACK);
138                scroller.setBorder(null);
139                container.getContentPane().add(scroller, BorderLayout.CENTER);
140
141                ((Component) container).addKeyListener(this);
142
143                this.slides = slides;
144
145                displayNextSlide();
146                pack();
147
148                ((Component) container).setVisible(true);
149        }
150
151        protected abstract void pack();
152
153        /**
154         * Display the next slide
155         * 
156         * @throws IOException
157         */
158        public void displayNextSlide() throws IOException {
159                if (currentSlideIndex < slides.size() - 1) {
160                        currentSlideIndex++;
161                        displaySlide(slides.get(currentSlideIndex));
162                }
163        }
164
165        /**
166         * Display the previous slide
167         * 
168         * @throws IOException
169         */
170        public void displayPrevSlide() throws IOException {
171                if (currentSlideIndex > 0) {
172                        currentSlideIndex--;
173                        displaySlide(slides.get(currentSlideIndex));
174                }
175        }
176
177        protected void displaySlide(Slide slide) throws IOException {
178                if (currentSlideComp != null) {
179                        contentPanel.remove(currentSlideComp);
180                        currentSlide.close();
181                }
182
183                currentSlide = slide;
184                currentSlideComp = currentSlide.getComponent(slideWidth, slideHeight);
185                currentSlideComp.setPreferredSize(new Dimension(slideWidth, slideHeight));
186                currentSlideComp.setMaximumSize(new Dimension(slideWidth, slideHeight));
187
188                contentPanel.add(currentSlideComp, new GridBagConstraints());
189
190                currentSlideComp.setFocusable(true);
191                currentSlideComp.requestFocus();
192                currentSlideComp.addKeyListener(this);
193                currentSlideComp.addMouseListener(new MouseAdapter() {
194                        @Override
195                        public void mouseClicked(MouseEvent e) {
196                                currentSlideComp.requestFocus();
197                        }
198                });
199
200                contentPanel.validate();
201                ((Component) container).repaint();
202        }
203
204        @Override
205        public void keyTyped(KeyEvent e) {
206                if (currentSlide instanceof KeyListener) {
207                        ((KeyListener) currentSlide).keyTyped(e);
208                }
209        }
210
211        @Override
212        public void keyPressed(KeyEvent e) {
213                try {
214                        switch (e.getKeyCode()) {
215                        case KeyEvent.VK_LEFT:
216                                displayPrevSlide();
217                                break;
218                        case KeyEvent.VK_RIGHT:
219                                displayNextSlide();
220                                break;
221                        case KeyEvent.VK_F:
222                                toggleFullscreen();
223                                break;
224                        case KeyEvent.VK_ESCAPE:
225                                setFullscreen(false);
226                                break;
227                        case KeyEvent.VK_Q:
228                                System.exit(0);
229                        }
230                } catch (final Exception ex) {
231                        ex.printStackTrace();
232                }
233
234                if (currentSlide instanceof KeyListener) {
235                        ((KeyListener) currentSlide).keyPressed(e);
236                }
237        }
238
239        private void toggleFullscreen() {
240                setFullscreen(!isFullscreen());
241        }
242
243        protected abstract boolean isFullscreen();
244
245        /**
246         * Method allows changing whether this window is displayed in fullscreen or
247         * windowed mode.
248         * 
249         * @param fullscreen
250         *            true = change to fullscreen, false = change to windowed
251         */
252        public abstract void setFullscreen(boolean fullscreen);
253
254        @Override
255        public void keyReleased(KeyEvent e) {
256                if (currentSlide instanceof KeyListener) {
257                        ((KeyListener) currentSlide).keyReleased(e);
258                }
259        }
260}