/* * Bibliothek - DockingFrames * Library built on Java/Swing, allows the user to "drag and drop" * panels containing any Swing-Component the developer likes to add. * * Copyright (C) 2012 Benjamin Sigg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Benjamin Sigg * benjamin_sigg@gmx.ch * CH - Switzerland */ package bibliothek.gui.dock.themes.basic; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import javax.swing.Timer; import bibliothek.gui.dock.station.span.Span; import bibliothek.gui.dock.station.span.SpanCallback; import bibliothek.gui.dock.station.span.SpanFactory; import bibliothek.gui.dock.station.span.SpanMode; /** * The {@link BasicSpanFactory} uses a small animation to expand and to shrink its {@link Span}s. * @author Benjamin Sigg */ public class BasicSpanFactory implements SpanFactory{ private int duration; private int minSpeed; private Timer timer; private Collection<BasicSpan> ticking = new HashSet<BasicSpan>(); /** * Creates a new factory * @param duration how long the animation takes * @param minSpeed the minimum speed, how many pixels must be shown/hidden on average within 1000 milliseconds. */ public BasicSpanFactory( int duration, int minSpeed ){ setDuration( duration ); setMinSpeed( minSpeed ); timer = new Timer( 0, new ActionListener(){ public void actionPerformed( ActionEvent e ){ tick(); } } ); timer.setRepeats( true ); timer.setCoalesce( true ); timer.setDelay( 20 ); timer.setInitialDelay( 0 ); } /** * Sets how long an animation takes for changing the size of a {@link Span}. Changing this property * affects all {@link Span}s that were ever created by this factory. * @param duration how long the animation is in milliseconds, at least 0 */ public void setDuration( int duration ){ if( duration < 0 ){ throw new IllegalArgumentException( "duration must be at least 0 milliseconds" ); } this.duration = duration; } /** * Sets the minimum speed to open/close spans. The minimum speed is the average amount of pixels * in which the size has to change within 1000 milliseconds. * @param minSpeed the minimum speed, can be 0 */ public void setMinSpeed( int minSpeed ){ this.minSpeed = minSpeed; } /** * Gets the minimum speed of the animation. * @return the minimum speed * @see #setMinSpeed(int) */ public int getMinSpeed(){ return minSpeed; } public Span create( SpanCallback callback ){ return new BasicSpan( callback ); } private synchronized void start( BasicSpan span ){ if( ticking.isEmpty() ){ ticking.add( span ); timer.start(); } else{ ticking.add( span ); } } private synchronized void stop( BasicSpan span ){ ticking.remove( span ); if( ticking.isEmpty() ){ timer.stop(); } } private synchronized void tick(){ long now = System.nanoTime(); for( BasicSpan span : ticking.toArray( new BasicSpan[ ticking.size() ] ) ){ span.tick( now ); } } private class BasicSpan implements Span { private SpanCallback callback; private Map<SpanMode, Integer> sizes = new HashMap<SpanMode, Integer>( 2 ); private SpanMode currentMode; private int sizeStart; private int sizeTarget; private int animationDuration = -1; private long animationStart = -1; private int duration; public BasicSpan( SpanCallback callback ){ this.callback = callback; } public void mutate( SpanMode mode ){ sizeStart = getSize(); sizeTarget = getSize( mode ); this.duration = BasicSpanFactory.this.duration; if( sizeStart != sizeTarget ){ if( minSpeed > 0 ){ duration = Math.min( duration, 1000 * Math.abs( sizeStart - sizeTarget ) / minSpeed ); } animationDuration = 0; animationStart = -1; start( this ); } } public void set( SpanMode mode ){ stop( this ); animationDuration = -1; sizeTarget = getSize( mode ); callback.resized(); } private int getSize( SpanMode mode ){ Integer size = sizes.get( mode ); if( size == null ){ return mode.getSize(); } else{ return size.intValue(); } } public void configureSize( SpanMode mode, int size ){ sizes.put( mode, size ); if( mode == currentMode ){ set( mode ); } } public void tick( long now ){ if( animationStart == -1 ){ animationStart = now; } animationDuration = (int)((now - animationStart) / 1000000); if( animationDuration >= duration ){ animationDuration = -1; stop( this ); } callback.resized(); } public int getSize(){ if( animationDuration == -1 ){ return sizeTarget; } double ratio = animationDuration / (double)duration; if( ratio > 1 ){ ratio = 1; } ratio = 2*(1-ratio)*ratio + ratio*ratio; return (int)(sizeStart * (1-ratio) + sizeTarget * ratio); } } }