001         package com.croftsoft.apps.savor;
002         
003         import java.awt.*;
004         import java.awt.event.*;
005         import java.io.*;
006         import java.util.*;
007         import java.util.concurrent.*;
008         import java.util.logging.*;
009    
010         import javax.imageio.ImageIO;
011    
012         import org.jdesktop.jdic.screensaver.ScreensaverContext;
013         import org.jdesktop.jdic.screensaver.ScreensaverSettings;
014         import org.jdesktop.jdic.screensaver.SimpleScreensaver;
015    
016         import com.croftsoft.core.awt.image.ImageLib;
017         import com.croftsoft.core.math.MathConstants;
018    
019         /*********************************************************************
020         * CroftSoft Savor.
021         *
022         * @version
023         *   $Id: SavorPix.java,v 1.17 2006/12/16 06:30:46 croft Exp $
024         * @since
025         *   2006-12-09
026         * @author
027         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
028         *********************************************************************/
029    
030         public final class  SavorPix
031           extends SimpleScreensaver
032         //////////////////////////////////////////////////////////////////////
033         //////////////////////////////////////////////////////////////////////
034         {
035           
036         private static final String
037           PROPERTY_SHOW  = "show",
038           PROPERTY_SPEED = "speed";
039           
040         private static final String [ ]  PROPERTY_SHOW_VALUES = {
041           "off",
042           "on" };
043         
044         private static final String [ ]  PROPERTY_SPEED_VALUES = {
045           "slow",
046           "medium",
047           "fast" };
048         
049         private static final int [ ]  DISPLAY_TIMES = {
050           600,
051           60,
052           6 };
053         
054         private static final boolean [ ]  SHOW_VALUES = {
055           false,
056           true };
057         
058         private static final long  DEFAULT_DISPLAY_TIME_NANOS
059           = DISPLAY_TIMES [ 0 ] * MathConstants.NANOSECONDS_PER_SECOND;
060         
061         private static final boolean  DEFAULT_SHOW
062           = SHOW_VALUES [ 0 ];
063         
064         private static final int  FRAME_PERIOD = 1;
065         
066         //
067         
068         private final Map<String, Integer>  speedMap;
069         
070         private final Map<String, Boolean>  showMap;
071         
072         private final Random  random;
073         
074         private final Logger  logger;
075         
076         //
077         
078         private boolean
079           initialized,
080           show,
081           skip;
082           
083         private int
084           componentWidth,
085           componentHeight;
086         
087         private Image  offscreenImage;
088         
089         private String [ ]  imageFilenames;
090         
091         private long
092           displayTimeNanos,
093           lastTimeNanos;
094         
095         //////////////////////////////////////////////////////////////////////
096         //////////////////////////////////////////////////////////////////////
097         
098         public  SavorPix ( )
099         //////////////////////////////////////////////////////////////////////
100         {
101           Logger.getLogger ( "org.jdesktop.jdic.screensaver" ).setLevel (
102             Level.FINEST );
103           
104           logger = Logger.getLogger ( getClass ( ).getName ( ) );
105           
106           logger.setLevel ( Level.FINEST );
107           
108           // Turn this on for debugging.
109           // initLoggingToFiles ( logger );
110           
111           random = new Random ( );
112           
113           speedMap = new HashMap<String, Integer> ( );
114           
115           for ( int  i = 0; i < PROPERTY_SPEED_VALUES.length; i++ )
116           {
117             speedMap.put (
118               PROPERTY_SPEED_VALUES [ i ],
119               new Integer ( DISPLAY_TIMES [ i ] ) );
120           }
121           
122           displayTimeNanos = DEFAULT_DISPLAY_TIME_NANOS;
123           
124           showMap = new HashMap<String, Boolean> ( );
125           
126           for ( int  i = 0; i < PROPERTY_SHOW_VALUES.length; i++ )
127           {
128             showMap.put (
129               PROPERTY_SHOW_VALUES [ i ],
130               new Boolean ( SHOW_VALUES [ i ] ) );
131           }
132           
133           show = DEFAULT_SHOW;
134         }
135         
136         //////////////////////////////////////////////////////////////////////
137         //////////////////////////////////////////////////////////////////////
138         
139         @Override
140         public synchronized void  init ( )
141         //////////////////////////////////////////////////////////////////////
142         {
143           try
144           {
145             logger.entering ( getClass ( ).getName ( ), "init" );
146    
147             lastTimeNanos = 0;
148    
149             final ScreensaverContext  screensaverContext = getContext ( );
150    
151             final Component  component = screensaverContext.getComponent ( );
152    
153             componentWidth  = component.getWidth  ( );
154    
155             componentHeight = component.getHeight ( );
156    
157             if ( ( componentWidth  > 0 )
158               && ( componentHeight > 0 ) )
159             {       
160               offscreenImage
161               = component.createImage ( componentWidth, componentHeight );
162             }
163    
164             // Might be called more than once
165    
166             if ( initialized )
167             {
168               return;
169             }
170    
171             initialized = true;
172    
173             // Spacebar skipping stopped working in the switch from Java 5
174             // to Java 6.  I do not know why.
175             
176             component.addKeyListener (
177               new KeyAdapter ( )
178               {
179                 @Override
180                 public void  keyPressed ( final KeyEvent  keyEvent )
181                 {
182                   final int  keyCode = keyEvent.getKeyCode ( );
183    
184                   if ( keyCode == KeyEvent.VK_SPACE )
185                   {
186                     logger.info ( "spacebar pressed" );
187    
188                     skip = true;
189                   }
190                 }
191               } );
192    
193             component.requestFocus ( );
194    
195             final Integer  displayTimeInteger
196               = ( Integer ) getPropertyValue ( PROPERTY_SPEED, speedMap );
197    
198             if ( displayTimeInteger != null )
199             {
200               displayTimeNanos = displayTimeInteger.intValue ( )
201               * MathConstants.NANOSECONDS_PER_SECOND;
202             }
203    
204             final Boolean  showBoolean
205             = ( Boolean ) getPropertyValue ( PROPERTY_SHOW, showMap );
206    
207             if ( showBoolean != null )
208             {
209               show = showBoolean.booleanValue ( );
210             }
211    
212             final Executor  executor = Executors.newSingleThreadExecutor ( );
213    
214             executor.execute (
215               new Runnable ( )
216               {
217                 public void  run ( )
218                 {
219                   imageFilenames = getImageFilenames ( );
220                 }
221               } );
222           }
223           catch ( final Throwable  throwable )
224           {
225             throwable.printStackTrace ( );
226             
227             logger.throwing ( getClass ( ).getName ( ), "init", throwable );
228           }
229           finally
230           {
231             logger.exiting ( getClass ( ).getName ( ), "init" );
232           }
233         }
234           
235         @Override
236         public void  paint ( final Graphics  graphics )
237         //////////////////////////////////////////////////////////////////////
238         {
239           try
240           {
241             if ( ( offscreenImage == null    )
242               || ( imageFilenames == null    )
243               || ( imageFilenames.length < 1 ) )
244             {
245               graphics.setColor ( Color.BLACK );
246    
247               graphics.fillRect (
248                 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE );
249    
250               return;
251             }
252    
253             final long  currentTimeNanos = System.nanoTime ( );
254    
255             final long  deltaTimeNanos = currentTimeNanos - lastTimeNanos;
256    
257             if ( skip
258               || ( deltaTimeNanos >= displayTimeNanos ) )
259             {
260               logger.info ( "paint changing image" );
261             
262               skip = false;
263    
264               lastTimeNanos = currentTimeNanos;
265    
266               final Graphics2D  offscreenGraphics2D
267                 = ( Graphics2D ) offscreenImage.getGraphics ( );
268    
269               offscreenGraphics2D.setColor ( Color.BLACK );
270    
271               offscreenGraphics2D.fillRect (
272                 0, 0, componentWidth, componentHeight );
273    
274               if ( ( imageFilenames != null    )
275                 && ( imageFilenames.length > 0 ) )
276               {
277                 final int  imageIndex
278                   = random.nextInt ( imageFilenames.length );
279    
280                 final String  imageFilename = imageFilenames [ imageIndex ];
281    
282                 logger.info ( imageFilename );
283                 
284                 final File  imageFile = new File ( imageFilename );
285    
286                 final Image  image = ImageIO.read ( imageFile );
287    
288                 final Rectangle  rectangle = ImageLib.shrinkToFitAndCenter (
289                   image.getWidth  ( null ),
290                   image.getHeight ( null ),
291                   componentWidth,
292                   componentHeight );
293    
294                 logger.info ( rectangle.toString ( ) );
295                 
296                 if ( image != null )
297                 {
298                   offscreenGraphics2D.drawImage (
299                     image,
300                     rectangle.x,
301                     rectangle.y,
302                     rectangle.width,
303                     rectangle.height,
304                     null );
305                 }
306                 else
307                 {
308                   logger.info ( "null image" );
309                 }
310    
311                 if ( show )
312                 {
313                   offscreenGraphics2D.setColor ( Color.GREEN );
314    
315                   offscreenGraphics2D.drawString ( imageFilename, 20, 20 );
316                 }
317               }
318               else
319               {
320                 logger.info ( "no images" );
321                 
322                 offscreenGraphics2D.setColor ( Color.RED );
323    
324                 offscreenGraphics2D.drawString ( "no images", 20, 20 );
325               }
326               
327               offscreenGraphics2D.dispose ( );
328    
329               graphics.drawImage ( offscreenImage, 0, 0, null );
330             }
331    
332             Thread.sleep ( FRAME_PERIOD );
333           }
334           catch ( final Throwable  throwable )
335           {
336             throwable.printStackTrace ( );
337             
338             logger.throwing ( getClass ( ).getName ( ), "paint", throwable );
339             
340             graphics.setColor ( Color.RED );
341             
342             graphics.fillRect ( 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE );
343             
344             graphics.setColor ( Color.BLACK );
345             
346             graphics.drawString ( throwable.getMessage ( ), 30, 30 );
347           }
348         }
349         
350         //////////////////////////////////////////////////////////////////////
351         // private methods
352         //////////////////////////////////////////////////////////////////////
353         
354         private String [ ]  getImageFilenames ( )
355         //////////////////////////////////////////////////////////////////////
356         {
357           try
358           {
359             final String [ ]  suffixes = ImageIO.getReaderFileSuffixes ( );
360             
361             final String  userHome = System.getProperty ( "user.home" );
362           
363             if ( userHome == null )
364             {
365               return null;
366             }
367             
368             final File  homeDirectory = new File ( userHome );
369             
370             if ( !homeDirectory.exists ( )
371               || !homeDirectory.isDirectory ( ) )
372             {
373               return null;
374             }
375           
376             File  picturesDirectory
377               = new File ( homeDirectory, "My Documents/My Pictures" );
378             
379             if ( !picturesDirectory.exists ( )
380               || !picturesDirectory.isDirectory ( ) )
381             {
382               picturesDirectory = homeDirectory;
383             }
384             
385             final Set<String>  imageFilenameSet = new HashSet<String> ( );
386             
387             final Stack<File>  directoryStack = new Stack<File> ( );
388             
389             directoryStack.add ( picturesDirectory );
390             
391             File  directory = null;
392             
393             while ( !directoryStack.isEmpty ( ) )
394             {
395               directory = directoryStack.pop ( );
396               
397               final String [ ]  list = directory.list ( );
398               
399               for ( String  filename : list )
400               {
401                 final File  file = new File ( directory, filename );
402                 
403                 if ( file.isDirectory ( ) )
404                 {
405                   directoryStack.add ( file );
406                   
407                   continue;
408                 }
409                 
410                 filename = filename.toLowerCase ( );
411                 
412                 for ( final String  suffix : suffixes )
413                 {
414                   if ( filename.endsWith ( "." + suffix ) )
415                   {
416                     filename = file.getCanonicalPath ( );
417                 
418                     imageFilenameSet.add ( filename );
419                     
420                     break;
421                   }
422                 }
423               }
424             }
425             
426             return imageFilenameSet.toArray ( new String [ 0 ] );
427           }
428           catch ( final Exception  ex )
429           {
430             ex.printStackTrace ( );
431           }
432           
433           return null;
434         }
435         
436         private Object  getPropertyValue (
437           final String  propertyName,
438           final Map     propertyMap )
439         //////////////////////////////////////////////////////////////////////
440         {
441           final ScreensaverContext  screensaverContext = getContext ( );
442           
443           final ScreensaverSettings  screensaverSettings
444             = screensaverContext.getSettings ( );
445           
446           String  propertyValue
447             = screensaverSettings.getProperty ( propertyName );
448           
449           if ( propertyValue == null )
450           {
451             return null;
452           }
453           
454           propertyValue = propertyValue.trim ( ).toLowerCase ( );
455           
456           return propertyMap.get ( propertyValue );
457         }
458         
459         private static void  initLoggingToFiles ( final Logger  logger )
460         //////////////////////////////////////////////////////////////////////
461         {
462           try
463           {
464             final String  userHome = System.getProperty ( "user.home" );
465    
466             if ( userHome == null )
467             {
468               return;
469             }
470    
471             final File  homeDirectory = new File ( userHome );
472    
473             if ( !homeDirectory.exists ( )
474               || !homeDirectory.isDirectory ( ) )
475             {
476               return;
477             }
478    
479             final File  logDirectory
480               = new File ( homeDirectory, ".croftsoft/savor" );
481    
482             logDirectory.mkdirs ( );
483    
484             final FileHandler  fileHandler = new FileHandler (
485               "%h/.croftsoft/savor/savor%g.log",
486               10000,
487               2,
488               false );
489    
490             Logger.getLogger ( "org.jdesktop.jdic.screensaver" ).addHandler (
491               fileHandler );
492    
493             logger.addHandler ( fileHandler );
494           }
495           catch ( final Exception  ex )
496           {
497             ex.printStackTrace ( );
498           }
499         }
500         
501         //////////////////////////////////////////////////////////////////////
502         //////////////////////////////////////////////////////////////////////
503         }