Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2016, Metron, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Metron, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.metsci.glimpse.layout;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.JScrollBar;
import com.metsci.glimpse.context.GlimpseBounds;
import com.metsci.glimpse.context.GlimpseTargetStack;
import net.miginfocom.layout.ComponentWrapper;
/**
* A GlimpseLayout that shifts its layout-children up or down based on a vertical-
* offset field, which can be set by calling the {@link #setVerticalOffset(int)}
* method.
*
* For an example of controlling the vertical-offset using a Swing scrollbar, see
* {@link com.metsci.glimpse.examples.layout.VerticallyScrollableLayoutExample}.
*
* In typical usage, when this layout's container is taller than minContentHeight,
* the child-layouts will not be scrollable -- this layout will size its child-
* layouts to fit the container.
*
* However, when this layout's container is shorter than minContentHeight, it will
* set child-layout heights to minContentHeight -- and verticalOffset then affects
* what portion of the content fits inside the container's bounds.
*
* @author hogye
*/
public class GlimpseVerticallyScrollableLayout extends GlimpseLayout
{
/**
* Returns a Runnable that, if called, will detach the layout from the scrollbar,
* removing all of the listeners put in place by the attach call.
*
* If you are doing all Glimpse stuff on the Swing thread, then this function causes
* no threading concerns.
*
* If you have multiple UI threads, you can generally rule out either deadlock or race
* conditions, but not both. This function rules out deadlock, so it must leave the door
* open to race conditions -- use at your own risk.
*
*/
public static Runnable attachScrollableToScrollbar( final GlimpseVerticallyScrollableLayout layout, final GlimpseTargetStack stack, final JScrollBar scrollbar )
{
final Runnable layoutListener = new Runnable( )
{
public void run( )
{
int layoutHeight;
int minContentHeight;
int verticalOffset;
layout.getLock( ).lock( );
try
{
layoutHeight = layout.getCurrentBounds( stack ).getHeight( );
minContentHeight = layout.getMinContentHeight( );
verticalOffset = layout.getVerticalOffset( );
}
finally
{
layout.getLock( ).unlock( );
}
int extent = layoutHeight;
int min = 0;
int max = max( minContentHeight, extent );
int value = min( verticalOffset, max - extent );
scrollbar.setValues( value, extent, min, max );
scrollbar.repaint( );
}
};
final AdjustmentListener scrollbarListener = new AdjustmentListener( )
{
public void adjustmentValueChanged( AdjustmentEvent ev )
{
layout.setVerticalOffset( scrollbar.getValue( ) );
}
};
// Attach listeners
layout.addListener( true, layoutListener );
scrollbar.addAdjustmentListener( scrollbarListener );
// Return a way to detach listeners later
return new Runnable( )
{
public void run( )
{
layout.removeListener( layoutListener );
scrollbar.removeAdjustmentListener( scrollbarListener );
}
};
}
protected int minContentHeight;
protected int verticalOffset;
// We rely on copy-on-write iteration semantics, so don't just declare as List
protected final CopyOnWriteArrayList listeners;
public GlimpseVerticallyScrollableLayout( int minContentHeight )
{
this.minContentHeight = minContentHeight;
this.verticalOffset = 0;
this.listeners = new CopyOnWriteArrayList<>( );
setLayoutManager( new GlimpseLayoutManager( )
{
public void layout( GlimpseLayoutDelegate parent )
{
int left = parent.getX( );
int contentWidth = parent.getWidth( );
int minContentHeight = GlimpseVerticallyScrollableLayout.this.minContentHeight;
int contentHeight = max( minContentHeight, parent.getHeight( ) + verticalOffset );
int top = parent.getY( ) + parent.getHeight( ) + verticalOffset;
int bottom = top - contentHeight;
for ( ComponentWrapper child : parent.getComponents( ) )
{
child.setBounds( left, bottom, contentWidth, contentHeight );
}
}
} );
}
public void addListener( boolean runImmediately, Runnable listener )
{
if ( runImmediately )
{
listener.run( );
}
listeners.add( listener );
}
public void removeListener( Runnable listener )
{
listeners.remove( listener );
}
protected void notifyListeners( )
{
for ( Runnable listener : listeners )
{
listener.run( );
}
}
@Override
public GlimpseBounds layoutTo( GlimpseTargetStack stack )
{
lock.lock( );
try
{
boolean wasDirty = isDirty( stack );
GlimpseBounds result = super.layoutTo( stack );
if ( wasDirty )
{
notifyListeners( );
}
return result;
}
finally
{
lock.unlock( );
}
}
public int getVerticalOffset( )
{
lock.lock( );
try
{
return verticalOffset;
}
finally
{
lock.unlock( );
}
}
public void setVerticalOffset( int verticalOffset )
{
lock.lock( );
try
{
if ( verticalOffset != this.verticalOffset )
{
this.verticalOffset = verticalOffset;
invalidateLayout( );
}
}
finally
{
lock.unlock( );
}
}
public int getMinContentHeight( )
{
lock.lock( );
try
{
return minContentHeight;
}
finally
{
lock.unlock( );
}
}
public void setMinContentHeight( int minContentHeight )
{
lock.lock( );
try
{
if ( minContentHeight != this.minContentHeight )
{
this.minContentHeight = minContentHeight;
invalidateLayout( );
}
}
finally
{
lock.unlock( );
}
}
public GlimpseBounds getCurrentBounds( GlimpseTargetStack stack )
{
lock.lock( );
try
{
GlimpseBounds bounds = layoutCache.getValueNoBoundsCheck( stack );
if ( bounds == null )
{
return layoutTo( stack );
}
else
{
return bounds;
}
}
finally
{
lock.unlock( );
}
}
}