/*
* 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) 2011 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.station.screen.magnet;
import java.util.ArrayList;
import java.util.List;
import bibliothek.gui.dock.station.screen.ScreenDockWindow;
import bibliothek.gui.dock.station.screen.magnet.MagnetRequest.Side;
/**
* The {@link DefaultMagnetOperation} searches the nearest attracted {@link ScreenDockWindow} to a moved side
* and if that other window is nearer than a given threshold then the moved window is attracted.
* @author Benjamin Sigg
*/
public class DefaultMagnetOperation implements MagnetOperation{
private int threshold;
/** Tells which window is neighbor of which other window */
private StickMagnetGraph graph;
/**
* Creates a new operation
* @param threshold the maximum distance between two items in pixels allowing them to interact with each other
*/
public DefaultMagnetOperation( int threshold ){
this.threshold = threshold;
}
public void attract( MagnetController controller, MagnetRequest request ){
if( graph == null ){
graph = new StickMagnetGraph( controller, request );
}
if( request.isMoved() ){
moved( controller, request );
}
else if( request.isResized() ){
resized( controller, request );
}
}
public void destroy(){
// nothing
}
/**
* Called if a window was moved.
* @param controller the caller
* @param request information about the window that was moved
*/
protected void moved( MagnetController controller, MagnetRequest request ){
ScreenDockWindow[] partners = controller.getAttracted( request.getWindow() );
int nearest = threshold;
ScreenDockWindow nearestWindow = null;
List<ScreenDockWindow> windows = new ArrayList<ScreenDockWindow>();
Side nearestSide = null;
for( ScreenDockWindow partner : partners ){
if( graph.getRoot().getNeighbor( partner ) == null ){
for( Side side : Side.values() ){
int distance = controller.distance( request.getWindow(), side, partner, side.opposite(), false );
if( distance <= threshold ){
boolean neighbors = false;
switch( side ){
case NORTH:
case SOUTH:
neighbors = controller.intersectVertically( request.getWindow(), partner, false );
break;
case EAST:
case WEST:
neighbors = controller.intersectHorizontally( request.getWindow(), partner, false );
break;
}
if( neighbors ){
if( distance < nearest ){
nearest = distance;
nearestWindow = partner;
nearestSide = side;
}
windows.add( partner );
}
}
}
}
}
if( nearestWindow != null ){
request.movingAttraction( nearestWindow, nearestSide, nearestSide.opposite() );
neighborMoved( controller, request, windows, nearestSide );
}
graph.moveNeighbors();
}
private void neighborMoved( MagnetController controller, MagnetRequest request, List<ScreenDockWindow> neighbors, Side side ){
Side checkA;
Side checkB;
if( side == Side.NORTH || side == Side.SOUTH ){
checkA = Side.EAST;
}
else{
checkA = Side.NORTH;
}
checkB = checkA.opposite();
ScreenDockWindow neighborA = null;
int distA = threshold+1;
for( ScreenDockWindow neighbor : neighbors ){
int dist = controller.distance( neighbor, checkA, request.getWindow(), checkA, false );
if( dist < distA ){
distA = dist;
neighborA = neighbor;
}
}
ScreenDockWindow neighborB = null;
int distB = threshold+1;
for( ScreenDockWindow neighbor : neighbors ){
int dist = controller.distance( neighbor, checkB, request.getWindow(), checkB, false );
if( dist < distB ){
distB = dist;
neighborB = neighbor;
}
}
if( distA <= distB && distA <= threshold ){
request.movingAttraction( neighborA, checkA, checkA );
}
else if( distB <= distA && distB <= threshold ){
request.movingAttraction( neighborB, checkB, checkB );
}
}
/**
* Called if a window was resized.
* @param controller the caller
* @param request information about the window that was resized
*/
protected void resized( MagnetController controller, MagnetRequest request ){
ScreenDockWindow[] partners = controller.getAttracted( request.getWindow() );
@SuppressWarnings("unchecked")
List<ScreenDockWindow>[] neighbors = new List[4];
for( Side side : Side.values() ){
int nearest = threshold+1;
ScreenDockWindow window = null;
List<ScreenDockWindow> windows = new ArrayList<ScreenDockWindow>();
neighbors[ side.ordinal() ] = windows;
for( ScreenDockWindow partner : partners ){
if( !request.is( side ) || graph.getRoot().getNeighbor( partner ) == null ){
int distance = controller.distance( request.getWindow(), side, partner, side.opposite(), false );
if( distance <= threshold || window == null ){
boolean neighbor = false;
switch( side ){
case NORTH:
case SOUTH:
neighbor = controller.intersectVertically( request.getWindow(), partner, false );
break;
case EAST:
case WEST:
neighbor = controller.intersectHorizontally( request.getWindow(), partner, false );
break;
}
if( neighbor ){
if( distance <= nearest || window == null ){
nearest = distance;
window = partner;
}
if( distance <= threshold ){
windows.add( partner );
}
}
}
}
}
if( window != null ){
if( request.is( side ) && nearest <= threshold ){
request.resizingAttraction( window, side, side.opposite() );
neighbors[side.ordinal()] = windows;
}
else if( nearest == 1 ){
neighbors[side.ordinal()] = windows;
}
}
}
for( Side side : Side.values() ){
if( request.is( side )){
neighborResized( controller, request, side, neighbors );
}
}
graph.moveAndResizeNeighbors();
}
private void neighborResized( MagnetController controller, MagnetRequest request, Side side, List<ScreenDockWindow>[] neighbors ){
Side checkA = null;
switch( side ){
case NORTH:
case SOUTH:
checkA = Side.EAST;
break;
case EAST:
case WEST:
checkA = Side.NORTH;
break;
}
Side checkB = checkA.opposite();
ScreenDockWindow neighborA = null;
int distanceA = threshold+1;
if( neighbors[checkA.ordinal()] != null ){
for( ScreenDockWindow neighbor : neighbors[ checkA.ordinal() ]){
if( !graph.depends( neighbor, side ) ){
int distance = controller.distance( request.getWindow(), side, neighbor, side, false );
if( distance < distanceA ){
distanceA = distance;
neighborA = neighbor;
}
}
}
}
ScreenDockWindow neighborB = null;
int distanceB = threshold+1;
if( neighbors[checkB.ordinal()] != null ){
for( ScreenDockWindow neighbor : neighbors[ checkB.ordinal() ]){
if( !graph.depends( neighbor, side ) ){
int distance = controller.distance( request.getWindow(), side, neighbor, side, false );
if( distance < distanceB ){
distanceB = distance;
neighborB = neighbor;
}
}
}
}
else{
distanceB = threshold+1;
}
if( distanceA <= distanceB && distanceA <= threshold ){
request.resizingAttraction( neighborA, side, side );
}
else if( distanceB <= distanceA && distanceB <= threshold ){
request.resizingAttraction( neighborB, side, side );
}
}
}