All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.tidalwave.swing.FadingPanel Maven / Gradle / Ivy

There is a newer version: 2.13.61
Show newest version
/***********************************************************************************************************************
 *
 * OpenBlueSky - NetBeans Platform Enhancements
 * Copyright (C) 2006-2012 by Tidalwave s.a.s. (http://www.tidalwave.it)
 *
 ***********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ***********************************************************************************************************************
 *
 * WWW: http://openbluesky.java.net
 * SCM: https://bitbucket.org/tidalwave/openbluesky-src
 *
 **********************************************************************************************************************/
package it.tidalwave.swing;

import it.tidalwave.swing.layout.CenterLayout;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;
import java.awt.CardLayout;
import java.awt.image.BufferedImage;
import java.awt.Image;
import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

//class AveragingKernel extends Kernel
//  {
//    public AveragingKernel (int size)
//      {
//        super(size, size, getData(size * size));   
//      }
//    
//    private static float[] getData (int size)
//      {
//        float[] data = new float[size];
//        
//        for (int i = 0; i < size; i++)
//          {
//            data[i] = 1.0f / size;  
//          }
//        
//        return data;
//      }
//  }

/*******************************************************************************
 *
 * @author  Fabrizio Giudici
 * @version $Id$
 *
 ******************************************************************************/
public class FadingPanel extends JPanel
  {
    private static final String CLASS = FadingPanel.class.getName();
    private static final Logger logger = Logger.getLogger(CLASS);
    
    /** How many frames per second are painted. */
    private static final int DEFAULT_TRANSITON_DURATION = 500;
    
    /** The overall duration of the transition. */
    private static final int FRAMES_PER_SECOND = 15;
    
    private static final String CONTENTS = "contents";
    
    private static final String TRANSITION = "transition";
    
    private int transitionDuration = DEFAULT_TRANSITON_DURATION;

    /** The contained component. */
    private JComponent content;
    
    /** The image which contains the snapshot of the previous contents. */
    private Image oldSnapshotImage;

    /** The image which contains the snapshot of the new contents. */
    private Image newSnapshotImage;

    /** The timer for the transition effect. */
    private Timer transitionTimer;

    /** The alpha component: 0.0 means the old snapshot is shown, 1.0 means the new contents are shown. */
    private float alpha = 1f;
    
    /** The alpha composite to blend the contents. */
    private AlphaComposite alphaComposite;
    
    /** The start time of the latest transition. */
    private long startTime;
    
    private CardLayout cardLayout = new CardLayout();
    
    private boolean locked;
    
    private boolean transitioning;
    
    private Timer waitingLabelDelayTimer;
    
    private int delayForWaitingLabel = 150;
    
    private final TransitionPanel transitionComponent = new TransitionPanel();
    
    private WaitingComponent waitingComponent;
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    public static interface Unlocker
      {
        public void run()
          throws Exception;
      }

    /***************************************************************************
     *
     * This component is able to paint a blend of the old and new snapshot image,
     * according to the current alphaComposite.
     *
     **************************************************************************/
    class TransitionPanel extends JPanel
      {
        public TransitionPanel()
          {
            setLayout(new CenterLayout());
          }
        
        @Override
        public void paint (final Graphics g)
          {
            final Graphics2D g2 = (Graphics2D)g;
            
            if (oldSnapshotImage == null)
              {
                content.paint(g);  
              }  
            else
              {
                g2.drawImage(oldSnapshotImage, 0, 0, null); 
              }
            
            if ((newSnapshotImage != null) && (alphaComposite != null))
              {
                final Composite compositeSave = g2.getComposite();
                g2.setComposite(alphaComposite);
                g2.drawImage(newSnapshotImage, 0, 0, null); 
                g2.setComposite(compositeSave);
              }
            
            super.paintComponents(g);
          }
      };
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private final ActionListener transitionTimerListener = new ActionListener()
      {
        public void actionPerformed (final ActionEvent event)
          {
            computeAlpha();
            logger.finer(">>>> Painting alpha = " + alpha);
            long time = System.currentTimeMillis();
            paintImmediately(getBounds());
            time = System.currentTimeMillis() - time;
            logger.finest(">>>>>>>> paintImmediately() took " + time + " msec");
          }
      };
          
    /***************************************************************************
     *
     *
     **************************************************************************/
    public FadingPanel (final JComponent content)
      {
        this.content = content;
        transitionTimer = new Timer(0, transitionTimerListener);
        transitionTimer.setCoalesce(true);
        transitionTimer.setDelay(1000 / FRAMES_PER_SECOND);
        setLayout(cardLayout);
        add(content, CONTENTS);
        add(transitionComponent, TRANSITION);
        setDoubleBuffered(true);
        setOpaque(false);
      }

    public void setTransitionDuration (final int transitionDuration) 
      {
        this.transitionDuration = transitionDuration;
      }
    
    public int getTransitionDuration() 
      {
        return transitionDuration;
      }

    /***************************************************************************
     *
     * Sets the initial delay before a "waiting" label is painted. If set to 0,
     * the label is painted immediately.
     * 
     * @param  delayForWaitingLabel  the delay
     *
     **************************************************************************/
    public void setDelayForWaitingLabel (final int delayForWaitingLabel)
      {
        this.delayForWaitingLabel = delayForWaitingLabel;
      }
    
    /***************************************************************************
     * 
     * Returns the initial delay.
     * 
     * @return  the initial delay
     *
     **************************************************************************/
    public int getDelayForWaitingLabel() 
      {
        return delayForWaitingLabel;
      }

    /***************************************************************************
     *
     *
     **************************************************************************/
    public void lock()
      {
        lock("Please wait...");
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    public void lock (final String message)
      {
        lock(createWaitingComponent(message));
      }
    
    /***************************************************************************
     *
     * Unlocks the component, taking a snapshot of the new contents and starting
     * an animated transition. This method is thread-safe: can be called in any
     * thread.
     *
     **************************************************************************/
    public void lock (final JComponent overlay)
      {
        if (!isVisible()) 
          {
            return;
          }

        if (SwingUtilities.isEventDispatchThread())
          {
            doLock(overlay);  
          }
        
        else
          {
            try
              {
                SwingUtilities.invokeAndWait(new Runnable()
                  {
                    public void run()
                      {
                        doLock(overlay);  
                      }
                  });
              } 
            catch (InvocationTargetException e)
              {
                logger.throwing(CLASS, "lock()", e);
              } 
            catch (InterruptedException e)
              {
                logger.throwing(CLASS, "lock()", e);
              }
          }
      }
    
    /***************************************************************************
     * 
     *
     **************************************************************************/
    public void prepareUnlock()
      {
        if (!isVisible()) 
          {
            return;
          }

        mustBeAWTThread();
        logger.finer(">>>> taking a snapshot of old content: " + content);
        oldSnapshotImage = takeSnapshot();
        paintImmediately(getBounds());
      }
    
    /***************************************************************************
     * 
     * Unlocks the component, taking a snapshot of the new contents and starting
     * an animated transition. 
     *
     **************************************************************************/
    public void unlock()
      {
        if (!isVisible()) 
          {
            return;
          }

        mustBeAWTThread();
        doUnlock();
      }
    
    /***************************************************************************
     * 
     * Unlocks the component, taking a snapshot of the new contents and starting
     * an animated transition. 
     *
     **************************************************************************/
    public void unlock (final Unlocker unlocker)
      {
        mustBeAWTThread();
        
        try
          {
            prepareUnlock(); 
            unlocker.run();
          }
        catch (Exception e)
          { 
            logger.throwing(CLASS, "unlock()", e);
          }
        finally
          {
            doUnlock();
          }
      }
//    public void unlock()
//      {
//        if (SwingUtilities.isEventDispatchThread())
//          {
//            doUnlock();
//          }
//        
//        else
//          {
//            try
//              {
//                SwingUtilities.invokeAndWait(new Runnable()
//                  {
//                    public void run()
//                      {
//                        doUnlock();
//                      }
//                  });
//              } 
//            catch (InvocationTargetException e)
//              {
//                logger.throwing(CLASS, "unlock()", e);
//              } 
//            catch (InterruptedException e)
//              {
//                logger.throwing(CLASS, "unlock()", e);
//              }
//          }
//      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    public boolean isLocked()
      {
        return locked;
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    @Override
    public void paint (final Graphics g)
      {
        super.paint(g);
        logger.fine("paint() - " + getBounds());
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    protected JComponent createWaitingComponent (final String message)
      {
        if (waitingComponent == null)
          {
            waitingComponent = new WaitingComponent();  
          }
        
        waitingComponent.setText(message);
        
        return waitingComponent;
      }

    /***************************************************************************
     *
     *
     **************************************************************************/
    private void doLock (final JComponent overlay)
      {
        logger.fine("doLock() - " + this);
        locked = true;
                
        if (delayForWaitingLabel == 0)
          {
            if (overlay != null)
              {
                overlay.setVisible(true);
              }
          }
        //
        // After a certain delay show the overlay.
        //
        else
          {
            if (overlay != null)
              {
                overlay.setVisible(false);
              }
            
            waitingLabelDelayTimer = new Timer(delayForWaitingLabel, new ActionListener()
              {
                public void actionPerformed (ActionEvent e)
                  {
                    if (locked) // still locked?
                      {
                        overlay.setVisible(true);
////                        paintImmediately(getBounds());
                      }
                  }
              });

            waitingLabelDelayTimer.setRepeats(false);
            waitingLabelDelayTimer.start();
          }
        
        if (transitionTimer.isRunning())
          {
            transitionTimer.stop();    
          }

        transitionComponent.removeAll();
        
        if (overlay != null)
          {
            transitionComponent.add(overlay);
          }
        
        cardLayout.show(this, TRANSITION);
      }

    /***************************************************************************
     * 
     *
     **************************************************************************/
    private void doUnlock()
      {
        logger.fine("doUnlock()");
        
//        if (!locked) // FIXME
//          {
//            startTransition();
//          }
        
        locked = false;
        startTime = System.currentTimeMillis();
//        computeAlpha();
        
        if ((waitingLabelDelayTimer != null) && waitingLabelDelayTimer.isRunning())
          {
            waitingLabelDelayTimer.stop();
          }

        repaint();
        final Timer ttt = new Timer(0, new ActionListener() 
          {
            public void actionPerformed (final ActionEvent event) 
              {
                logger.finer(">>>> taking a snapshot of new content: " + content);
                newSnapshotImage = takeSnapshot();
                transitioning = true;
                transitionTimer.start();
              }
          });
        ttt.setRepeats(false);
        ttt.setInitialDelay(100);
        ttt.start();
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private void computeAlpha()
      {
        logger.finer("computeAlpha()");
        final long deltaTime = System.currentTimeMillis() - startTime;
        alpha = (float)Math.max(0.0, Math.min(1.0, (double)deltaTime / transitionDuration));

        alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);

        if (alpha == 1.0)
          {
            logger.fine(">>>> alpha is 1.0, stopping transitionTimer, restoring contents");
            transitionTimer.stop();  
            oldSnapshotImage = null;
            newSnapshotImage = null;
            alphaComposite = null;
            transitioning = false;
//            content.setIgnoreRepaint(false);
            cardLayout.show(this, CONTENTS);

            transitionComponent.removeAll();
          }
      }
    
    /***************************************************************************
     *
     * Takes a snapshot of the current contents, with the given overlay.
     *
     * @param  overlay  the overlay (null if none)
     * @return           
     *
     **************************************************************************/
    private Image takeSnapshot()
      {
        logger.fine("takeSnapshot()");
        
        if (!isValid())
          {
            logger.warning("Something is wrong, takeSnapshot() but I'm not valid");  
          }
        
        final int width = content.getWidth();
        final int height = content.getHeight();
        logger.finest(">>>> snapshot size: " + width + " x " + height);
        final BufferedImage image =  createCompatibleImage(Math.max(1, width), Math.max(1, height));
        // TODO: use VolatileImage?
        
        final Graphics g = image.getGraphics();
        
        try
          {
            //content.doLayout();
            content.paint(g);
          }
        finally
          {
            g.dispose();
          }
        
        return image;
      }

    /***************************************************************************
     *
     *
     **************************************************************************/
    private static BufferedImage createCompatibleImage (final int width, final int height)
      {
        final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        final GraphicsDevice gs = ge.getDefaultScreenDevice();
        final GraphicsConfiguration gc = gs.getDefaultConfiguration();
 
        return gc.createCompatibleImage(width, height);
      }
    
    /***************************************************************************
     *
     *
     **************************************************************************/
    private void mustBeAWTThread() 
      {
        if (!SwingUtilities.isEventDispatchThread())
          {
            throw new IllegalStateException("Must be called by the AWT-Thread");  
          }
      }
  }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy