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}