001 /** 002 * iFish - An iRiver iHP jukebox database creation tool 003 * 004 * Copyright (C) 2009 Richard "Shred" Körber 005 * http://ifish.shredzone.org 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 019 */ 020 package net.shredzone.ifish.gui; 021 022 import net.shredzone.ifish.*; 023 import net.shredzone.ifish.i18n.L; 024 import net.shredzone.ifish.pool.ImgPool; 025 import net.shredzone.jshred.swing.*; 026 import javax.swing.*; 027 import java.text.*; 028 import java.awt.*; 029 import java.io.*; 030 import java.util.*; 031 032 /** 033 * An About pane for IFish. 034 * 035 * @author Richard Körber <dev@shredzone.de> 036 * @version $Id: AboutPane.java 291 2009-04-28 21:29:27Z shred $ 037 */ 038 public class AboutPane extends JPanel implements net.shredzone.ifish.Version { 039 private static final long serialVersionUID = 3258412837389742389L; 040 private final BubblePane jpTop; 041 042 public AboutPane() { 043 setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) ); 044 045 //--- Logo --- 046 jpTop = new BubblePane(); 047 jpTop.setLayout( new BorderLayout() ); 048 { 049 JLabel jlIcon = new JLabel( ImgPool.get( "ifish-logo.png" ) ); 050 jlIcon.setBorder( BorderFactory.createEmptyBorder( 10,10,10,10 ) ); 051 jpTop.add( jlIcon, BorderLayout.CENTER ); 052 } 053 jpTop.setBorder( BorderFactory.createEtchedBorder() ); 054 SwingUtils.setMinimumHeight( jpTop ); 055 add( jpTop ); 056 057 //--- Copyright --- 058 final JPanel jpCopyright = new JPanel( new BorderLayout() ); 059 { 060 Object[] args = new Object[] { 061 Version.VERSION, 062 new Date(), 063 "http://www.shredzone.net/go/ifish", 064 }; 065 066 String message = MessageFormat.format( L.tr("about.headline"), args ); 067 JLabel jlCopyright = new JLabel( message, JLabel.CENTER ); 068 jpCopyright.add( jlCopyright, BorderLayout.CENTER ); 069 } 070 jpCopyright.setBorder( BorderFactory.createEmptyBorder( 5,5,5,5 ) ); 071 SwingUtils.setMinimumHeight( jpCopyright ); 072 add( jpCopyright ); 073 074 //--- Kudos --- 075 try { 076 //--- Get the HTML code --- 077 final java.net.URL about = net.shredzone.ifish.i18n.L.class.getResource("about.html"); 078 StringBuffer buff = new StringBuffer(); 079 BufferedReader reader = null; 080 try { 081 reader = new BufferedReader( new InputStreamReader( about.openStream(), "UTF-8" ) ); 082 String line; 083 while( (line = reader.readLine()) != null ) { 084 buff.append( line ); 085 } 086 }finally { 087 if( reader!=null ) 088 reader.close(); 089 } 090 091 //--- Replace placeholders with translation --- 092 int pos, end = 0; 093 while( (pos = buff.indexOf("<?", end)) >= 0 ) { 094 end = buff.indexOf("?>",pos+2); 095 String key = "about.html." + buff.substring( pos+2, end ).trim(); 096 buff.replace( pos, end+2, L.tr(key) ); 097 } 098 099 //--- Show it --- 100 add( new JScrollPane( new Kudos( buff.toString() ) ) ); 101 }catch( Exception e ) {} // should not happen, so make no fuss about it 102 } 103 104 /** 105 * Start the bubbling, when the component's dimensions are known. 106 */ 107 public void bubble() { 108 jpTop.initBubbles(); 109 } 110 111 /** 112 * Note that the AboutPane window has been closed, so the AboutPane 113 * can free its resources. 114 */ 115 public void closed() { 116 jpTop.stopBubbles(); 117 } 118 119 /*--------------------------------------------------------------------*/ 120 121 /** 122 * This is a simple JLabel that shows a text on a white background, with 123 * word wrapping enabled. It is a replacement for JEditorPane which took 124 * MUCH too long to load just for showing a kudos list. 125 */ 126 private static class Kudos extends JLabel implements Scrollable { 127 private static final long serialVersionUID = -2524106857919046866L; 128 129 /** 130 * Create a new Kudos object. 131 * 132 * @param txt Text to be shown, may be HTML. 133 */ 134 public Kudos( String txt ) { 135 super( txt ); 136 setBackground( Color.WHITE ); 137 setOpaque( true ); 138 } 139 140 @Override 141 public Dimension getPreferredScrollableViewportSize() { 142 return getPreferredSize(); 143 } 144 145 @Override 146 public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction ) { 147 return 12; 148 } 149 150 @Override 151 public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction ) { 152 return 40; 153 } 154 155 @Override 156 public boolean getScrollableTracksViewportWidth() { 157 return true; 158 } 159 160 @Override 161 public boolean getScrollableTracksViewportHeight() { 162 return false; 163 } 164 } 165 166 /*--------------------------------------------------------------------*/ 167 168 /** 169 * A BubblePane is a pane with a gradient and bubbles floating up 170 * in the background. 171 */ 172 private final static class BubblePane extends JGradientPanel implements Runnable { 173 private static final long serialVersionUID = 3257567321504757817L; 174 private final static int DELAY = 70; // ms delay between each animation phase 175 private final static int NUM_BUBBLES = 10; // number of bubbles 176 private final static int NUM_STREAKS = 40; // number of streaks 177 178 private boolean running = true; 179 private final Bubble[] bubbles = new Bubble[NUM_BUBBLES]; 180 private final Streak[] streaks = new Streak[NUM_STREAKS]; 181 182 /** 183 * Create a new BubblePane 184 */ 185 public BubblePane() { 186 super( 187 new Color( 34, 206, 189 ), // Deep sea gradient :) 188 new Color( 34, 104, 206 ) 189 ); 190 } 191 192 /** 193 * Initialize the bubbles before drawing them the first time 194 */ 195 public void initBubbles() { 196 final Random rnd = new Random(); 197 for( int ix=0; ix<NUM_BUBBLES; ix++ ) { 198 bubbles[ix] = new Bubble( rnd, this ); 199 } 200 for( int ix=0; ix<NUM_STREAKS; ix++ ) { 201 streaks[ix] = new Streak( rnd, this ); 202 } 203 final Thread th = new Thread( this, "Bubbles" ); 204 th.start(); 205 } 206 207 /** 208 * Stop bubble drawing 209 */ 210 public void stopBubbles() { 211 running = false; 212 } 213 214 /** 215 * Paint the bubbles 216 * 217 * @param g Graphics context 218 */ 219 @Override 220 protected void paintComponent( Graphics g ) { 221 final Graphics2D g2d = (Graphics2D) g.create(); 222 g2d.addRenderingHints( new RenderingHints( 223 RenderingHints.KEY_ANTIALIASING, 224 RenderingHints.VALUE_ANTIALIAS_ON 225 )); 226 g2d.scale( 0.5d, 0.5d ); 227 for( int ix=0; ix<NUM_STREAKS; ix++ ) { 228 streaks[ix].draw( g2d ); 229 } 230 for( int ix=0; ix<NUM_BUBBLES; ix++ ) { 231 bubbles[ix].draw( g2d ); 232 } 233 g2d.dispose(); 234 } 235 236 /** 237 * Thread: move the bubbles 238 */ 239 @Override 240 public void run() { 241 try { 242 final Toolkit tk = Toolkit.getDefaultToolkit(); 243 while( running ) { 244 Thread.sleep( DELAY ); 245 for( int ix=0; ix<NUM_BUBBLES; ix++ ) { 246 bubbles[ix].advance(); 247 } 248 for( int ix=0; ix<NUM_STREAKS; ix++ ) { 249 streaks[ix].advance(); 250 } 251 repaint(); 252 tk.sync(); 253 } 254 }catch( Exception e ) {} 255 } 256 257 } 258 259 /*--------------------------------------------------------------------*/ 260 261 /** 262 * This class represents a single bubble 263 */ 264 private final static class Bubble { 265 private final Random rnd; 266 private final BubblePane pane; 267 private int x, y; // Current X (800) and Y (y*16) 268 private int amp, freq, per; // Amplitude, frequency and period 269 private int size; // Size of the bubble 270 private int speed; // Speed 271 272 /** 273 * Create a bubble. 274 * 275 * @param rnd Random generator to be used 276 * @param pane Reference to the bubble pane 277 */ 278 public Bubble( Random rnd, BubblePane pane ) { 279 this.rnd = rnd; 280 this.pane = pane; 281 x = rnd.nextInt(800); // x between 0 and 799 282 y = rnd.nextInt(pane.getHeight()*2*16); // y between 0 and height*2 283 size = rnd.nextInt(8) + 4; // size between 4 and 11 284 amp = rnd.nextInt(12); // amplitude between 0 and 11 285 freq = rnd.nextInt(8); // frequency between 0 and 7 286 per = rnd.nextInt(256); // Period between 0 and 255 287 speed = rnd.nextInt(26) + 10; // Speed between 10 and 35 288 } 289 290 /** 291 * Draw this bubble to the Graphics context. 292 * 293 * @param g Graphics context 294 */ 295 public void draw( Graphics g ) { 296 g.setColor( new Color( 240,250,255, 255*size/20 ) ); 297 g.drawOval( 298 (int) ( ( x * pane.getWidth() *2 / 800) + Math.round( amp*Math.sin( 2*per*Math.PI/256 ) ) ), 299 (int) ( y/16 ), 300 size, 301 size 302 ); 303 } 304 305 /** 306 * Advance the bubble one step upward. 307 */ 308 public void advance() { 309 per = (freq+per) % 256; 310 y -= speed; 311 if( y<-12 ) { 312 x = rnd.nextInt(800); // x between 0 and 800 313 y = pane.getHeight()*2*16; // y between 0 and height 314 freq = rnd.nextInt(8); // frequency between 0 and 7 315 speed = rnd.nextInt(26) + 10; // Speed between 10 and 35 316 } 317 } 318 319 } 320 321 /*--------------------------------------------------------------------*/ 322 323 /** 324 * This class represents a single light streak 325 */ 326 private final static class Streak { 327 private final static int ANGLE = 12; 328 329 private final Random rnd; 330 private final BubblePane pane; 331 private int x; // X 332 private int amp, freq, per; // Amplitude, frequency and period 333 private int size; // Size of the bubble 334 335 /** 336 * Create a light streak. 337 * 338 * @param rnd Random generator to be used 339 * @param pane Reference to the bubble pane 340 */ 341 public Streak( Random rnd, BubblePane pane ) { 342 this.rnd = rnd; 343 this.pane = pane; 344 x = rnd.nextInt(800); // x between 0 and 799 345 size = rnd.nextInt(30) + 10; // size between 10 and 40 346 amp = rnd.nextInt(24); // amplitude between 0 and 24 347 freq = rnd.nextInt(4)+1; // frequency between 1 and 5 348 per = rnd.nextInt(256); // Period between 0 and 255 349 } 350 351 /** 352 * Draw this streak to the Graphics context. 353 * 354 * @param g Graphics context 355 */ 356 public void draw( Graphics g ) { 357 g.setColor( new Color( 255,255,128, (int) Math.abs( 25 * Math.sin(2*per*Math.PI/128)) )); 358 final int h = pane.getHeight() * 2; 359 final int cx = (int) ( ( x * pane.getWidth() *2 / 800) + Math.round( amp*Math.sin( 2*per*Math.PI/256 ) ) ); 360 Polygon p = new Polygon(); 361 p.addPoint( cx , 0 ); 362 p.addPoint( cx+size , 0 ); 363 p.addPoint( cx+size+ANGLE, h ); 364 p.addPoint( cx+ANGLE , h ); 365 g.fillPolygon( p ); 366 } 367 368 /** 369 * Advance the streak one step. 370 */ 371 public void advance() { 372 per = (freq+per) % 256; 373 } 374 375 } 376 377 } 378