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        private boolean hidden = false;
078
079        /**
080         * Default constructor.
081         *
082         * @param container
083         *            the root window
084         * @param slides
085         *            the slides
086         * @param slideWidth
087         *            the width to display the slides
088         * @param slideHeight
089         *            the height to display the slides
090         * @param background
091         *            a background image to display behind the slides (the slides need
092         *            to be transparent!)
093         * @throws IOException
094         *             if the first slide can't be loaded
095         */
096        public Slideshow(RootPaneContainer container, List<Slide> slides, final int slideWidth, final int slideHeight,
097                        BufferedImage background)
098                        throws IOException
099        {
100                this.container = container;
101
102                this.slideWidth = slideWidth;
103                this.slideHeight = slideHeight;
104
105                final BufferedImage bg;
106                if (background == null) {
107                        bg = new BufferedImage(slideWidth, slideHeight, BufferedImage.TYPE_3BYTE_BGR);
108                        final Graphics2D g = bg.createGraphics();
109                        g.setColor(UIManager.getColor("Panel.background"));
110                        g.fillRect(0, 0, bg.getWidth(), bg.getHeight());
111                } else {
112                        bg = background;
113                }
114
115                contentPanel = new JPanel() {
116                        private static final long serialVersionUID = 1L;
117
118                        @Override
119                        public void paintComponent(Graphics g) {
120                                super.paintComponent(g);
121                                g.drawImage(bg, 0, 0, slideWidth, slideHeight, null);
122                        };
123
124                        @Override
125                        public void paint(Graphics g) {
126                                if (!hidden)
127                                        super.paint(g);
128                        }
129                };
130                contentPanel.setOpaque(false);
131                contentPanel.setSize(slideWidth, slideHeight);
132                contentPanel.setPreferredSize(new Dimension(slideWidth, slideHeight));
133                contentPanel.setLayout(new GridBagLayout());
134
135                final JPanel scrollContent = new JPanel();
136                scrollContent.setLayout(new GridBagLayout());
137                scrollContent.setSize(contentPanel.getSize());
138                scrollContent.setPreferredSize(contentPanel.getSize());
139                scrollContent.add(contentPanel);
140                scrollContent.setBackground(Color.BLACK);
141
142                container.getContentPane().setBackground(Color.BLACK);
143
144                final JScrollPane scroller = new JScrollPane(scrollContent);
145                scroller.setBackground(Color.BLACK);
146                scroller.setBorder(null);
147                container.getContentPane().add(scroller, BorderLayout.CENTER);
148
149                ((Component) container).addKeyListener(this);
150
151                this.slides = slides;
152
153                displayNextSlide();
154                pack();
155
156                ((Component) container).setVisible(true);
157        }
158
159        protected abstract void pack();
160
161        /**
162         * Display the next slide
163         *
164         * @throws IOException
165         */
166        public void displayNextSlide() throws IOException {
167                if (currentSlideIndex < slides.size() - 1) {
168                        currentSlideIndex++;
169                        displaySlide(slides.get(currentSlideIndex));
170                }
171        }
172
173        /**
174         * Display the previous slide
175         *
176         * @throws IOException
177         */
178        public void displayPrevSlide() throws IOException {
179                if (currentSlideIndex > 0) {
180                        currentSlideIndex--;
181                        displaySlide(slides.get(currentSlideIndex));
182                }
183        }
184
185        protected void displaySlide(Slide slide) throws IOException {
186                if (currentSlideComp != null) {
187                        contentPanel.remove(currentSlideComp);
188                        currentSlide.close();
189                }
190
191                currentSlide = slide;
192                currentSlideComp = currentSlide.getComponent(slideWidth, slideHeight);
193                currentSlideComp.setPreferredSize(new Dimension(slideWidth, slideHeight));
194                currentSlideComp.setMaximumSize(new Dimension(slideWidth, slideHeight));
195
196                contentPanel.add(currentSlideComp, new GridBagConstraints());
197
198                currentSlideComp.setFocusable(true);
199                currentSlideComp.requestFocus();
200                currentSlideComp.addKeyListener(this);
201                currentSlideComp.addMouseListener(new MouseAdapter() {
202                        @Override
203                        public void mouseClicked(MouseEvent e) {
204                                currentSlideComp.requestFocus();
205                        }
206                });
207
208                contentPanel.validate();
209                ((Component) container).repaint();
210        }
211
212        @Override
213        public void keyTyped(KeyEvent e) {
214                if (currentSlide instanceof KeyListener) {
215                        ((KeyListener) currentSlide).keyTyped(e);
216                }
217        }
218
219        @Override
220        public void keyPressed(KeyEvent e) {
221                try {
222                        switch (e.getKeyCode()) {
223                        case KeyEvent.VK_LEFT:
224                                if (!hidden)
225                                        displayPrevSlide();
226                                break;
227                        case KeyEvent.VK_PAGE_UP:
228                                if (!hidden)
229                                        displayPrevSlide();
230                                break;
231                        case KeyEvent.VK_UP:
232                                if (!hidden)
233                                        displayPrevSlide();
234                                break;
235                        case KeyEvent.VK_RIGHT:
236                                if (!hidden)
237                                        displayNextSlide();
238                                break;
239                        case KeyEvent.VK_PAGE_DOWN:
240                                if (!hidden)
241                                        displayNextSlide();
242                                break;
243                        case KeyEvent.VK_DOWN:
244                                if (!hidden)
245                                        displayNextSlide();
246                                break;
247                        case KeyEvent.VK_F:
248                                toggleFullscreen();
249                                break;
250                        case KeyEvent.VK_F5:
251                                if (e.isShiftDown())
252                                        toggleFullscreen();
253                                break;
254                        case KeyEvent.VK_ESCAPE:
255                                setFullscreen(false);
256                                break;
257                        case KeyEvent.VK_B:
258                                System.out.println(this.hidden);
259                                if (!e.isShiftDown()) {
260                                        this.hidden = !this.hidden;
261                                        this.contentPanel.repaint();
262                                }
263                                break;
264                        case KeyEvent.VK_Q:
265                                System.exit(0);
266                        }
267                } catch (final Exception ex) {
268                        ex.printStackTrace();
269                }
270
271                if (currentSlide instanceof KeyListener) {
272                        ((KeyListener) currentSlide).keyPressed(e);
273                }
274        }
275
276        private void toggleFullscreen() {
277                setFullscreen(!isFullscreen());
278        }
279
280        protected abstract boolean isFullscreen();
281
282        /**
283         * Method allows changing whether this window is displayed in fullscreen or
284         * windowed mode.
285         *
286         * @param fullscreen
287         *            true = change to fullscreen, false = change to windowed
288         */
289        public abstract void setFullscreen(boolean fullscreen);
290
291        @Override
292        public void keyReleased(KeyEvent e) {
293                if (currentSlide instanceof KeyListener) {
294                        ((KeyListener) currentSlide).keyReleased(e);
295                }
296        }
297}