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 &lt;dev@shredzone.de&gt;
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