/*
* ThumbnailDraggableManager.java
*
* Copyright (C) 2006-2007 Gabriel Burca (gburca dash virtmus at ebixio dot com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.ebixio.thumbviewer;
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import java.awt.event.ContainerEvent;
import java.util.HashSet;
import java.util.Set;
import net.java.swingfx.jdraggable.DragPolicy;
import net.java.swingfx.jdraggable.Draggable;
import net.java.swingfx.jdraggable.DraggableListener;
import net.java.swingfx.jdraggable.DraggableManager;
import net.java.swingfx.jdraggable.DraggableMask;
/**
* @author Gabriel Burca <gburca dash virtmus at ebixio dot com>
* Needed to implement DraggableManager (instead of just extending DefaultDraggableManager)
* because the default manager did not remove the mouse listeners from the component when
* the component was removed from the container.
*/
public class ThumbnailDraggableManager implements DraggableManager {
/**
* the {@link Container} which contains {@link Component}'s, which may
* or may not, implement the {@link Draggable} interface
*/
private Container draggableContainer;
/**
* maintains whether a "Draggable Container" has been registered or not
*/
private boolean draggableContainerRegistered;
/**
* the component which was chosen to be dragged
*/
private Draggable hitDraggable;
/**
* maintains the state of <code>hitDraggable</cdoe>
*/
private byte draggableState;
/**
* the {@link DragPolicy} to abide by
*/
private DragPolicy dragPolicy;
/**
* the listener which provides the real ability for a component to
* change its location during a "drag"
*/
private DraggableListener dragListener;
/**
* maintains a {@link java.util.Set} of the components
* which have had a {@link DraggableListener} added to them.
* This is only used for "cleanup".
* This implementation stores the hash code of each component.
*/
private Set<Integer> hearingComponents;
/**
* determines whether the "draggable container" layout manager should be
* set to <code>null</code> once a component is dragged (this allows the components
* to maintain their position even if the container is resized), or not.
*/
private boolean nullifyLayout = true;
/** The starting location of the draggable component */
private Point startLocation;
/**
* Creates a new {@link DraggableManager} with no "Draggable Container"
* registered
*
* @see net.java.swingfx.jdraggable.DefaultDraggableManager#DefaultDraggableManager(Container)
* @see net.java.swingfx.jdraggable.DefaultDraggableManager#registerDraggableContainer(Container)
*/
public ThumbnailDraggableManager() {
}
/**
* Creates a new {@link DraggableManager} and registers
* <code>draggableContainer</code> as the "Draggable Container"
*
* @param draggableContainer the "Draggable Container" to register
*
* @throws IllegalArgumentException if <code>draggableContainer</code> is
* <code>null</code>
*
* @see net.java.swingfx.jdraggable.DefaultDraggableManager#DefaultDraggableManager()
* @see net.java.swingfx.jdraggable.DefaultDraggableManager#registerDraggableContainer(Container)
*/
public ThumbnailDraggableManager(Container draggableContainer) {
if (draggableContainer == null) {
throw new IllegalArgumentException("Can not register a null Draggable Container");
}
registerDraggableContainer(draggableContainer);
}
/* (non-Javadoc)
* @see com.codecraig.jdraggable.DraggableManager#setNullifyLayout(boolean)
*/
@Override
public void setNullifyLayout(boolean nullifyLayout) {
this.nullifyLayout = nullifyLayout;
}
/* (non-Javadoc)
* @see com.codecraig.jdraggable.DraggableManager#shouldNullifyLayout()
*/
@Override
public boolean shouldNullifyLayout() {
return nullifyLayout;
}
/* (non-Javadoc)
* @see com.codecraig.jdraggable.DraggableManager#startDrag(java.awt.Component)
*/
@Override
public boolean startDrag(Component componentToDrag) {
if (isDraggableContainerRegistered()) {
if (getDragPolicy().isDraggable(componentToDrag)) {
hitDraggable = (componentToDrag instanceof Draggable ? (Draggable) componentToDrag : new DraggableMask(componentToDrag));
setState(STATE_STILL);
startLocation = componentToDrag.getLocation();
return true;
}
}
return false;
}
/* (non-Javadoc)
* @see com.codecraig.jdraggable.DraggableManager#dragging()
*/
@Override
public boolean dragging() {
if (isDraggableContainerRegistered()) {
if (hitDraggable != null) {
setState(STATE_DRAGGING);
return true;
}
}
return false;
}
/* (non-Javadoc)
* @see com.codecraig.jdraggable.DraggableManager#stopDrag()
*/
@Override
public boolean stopDrag() {
if (isDraggableContainerRegistered()) {
// Only reorder thumbs if they've been moved by a significant amount
Component comp = hitDraggable.getComponent();
hitDraggable = null;
setState(STATE_UNKNOWN);
double distance = comp.getLocation().distance(startLocation);
if (distance < Math.min(comp.getWidth()/2, comp.getHeight()/2)) {
comp.setLocation(startLocation);
} else {
ThumbsTopComponent.findInstance().reorderThumbs();
}
return true;
}
return false;
}
/**
* Returns the {@link Container} which registered itself as the
* "Draggable Container" with this {@link DraggableManager}
*
* @return the "Draggable Container" or <code>null</code> if not
* <code>Container</code> has been registered as the
* "Draggable Container"
*/
@Override
public Container getDraggableContainer() {
return draggableContainer;
}
private boolean isDraggableContainerRegistered() {
return draggableContainerRegistered;
}
/**
* Sets the state of <code>hitDraggable</code>
*
* @param state the state of <code>hitDraggable</code>
*/
private void setState(byte state) {
draggableState = state;
}
/**
* Returns the state of the current {@link Draggable} component which this
* manager is handling
*
* @param draggableComponent The draggable component.
* @return the state of the current <code>Draggable</code> component
*
* @see net.java.swingfx.jdraggable.DraggableManager#getState(net.java.swingfx.jdraggable.Draggable)
*/
@Override
public byte getState(Draggable draggableComponent) {
return draggableState;
}
/**
* Registers the given {@link Container} as the "Draggable Container"
*
* @param draggableContainer the <code>Container</code> whose <code>Draggable</code>
* components should be able to be dragged
*
* @see DraggableManager#registerDraggableContainer(java.awt.Container)
*
* @throws IllegalArgumentException if a <code>Container</code> has already
* been registered
*/
@Override
public void registerDraggableContainer(Container draggableContainer) {
if (this.draggableContainer == null) {
this.draggableContainer = draggableContainer;
draggableContainer.addContainerListener(this);
dragListener = new DraggableListener(this);
hearingComponents = new HashSet<Integer>();
draggableContainerRegistered = true;
} else {
throw new IllegalArgumentException("A Draggable Container has already been registered");
}
}
/**
* Un-Registers the given {@link Container} from being the "Draggable Container"
*
* @param draggableContainer the <code>Container</code> to unregister
*
* @see DraggableManager#unregisterDraggableContainer(Container)
*
* @throws IllegalArgumentException if the given container is not the same
* as the already registered container
* @throws IllegalStateException if no container is currently registered
*/
@Override
public void unregisterDraggableContainer(Container draggableContainer) {
if (this.draggableContainer == null) {
throw new IllegalStateException("Failed to unregister draggable container," +
" since no draggable container was registered");
}
if (this.draggableContainer.equals(draggableContainer)) {
this.draggableContainer.removeContainerListener(this);
cleanupHearingComponents();
this.dragListener = null;
this.draggableContainer = null;
draggableContainerRegistered = false;
} else {
throw new IllegalArgumentException("Failed to unregister draggable container," +
" the given Container is not the same as the" +
" register draggable container");
}
}
/**
* Removes the listeners from "hearing components"
*/
private void cleanupHearingComponents() {
int count = draggableContainer.getComponentCount();
for (int i = count - 1; i >= 0 && hearingComponents.size() > 0; i--) {
Component c = draggableContainer.getComponent(i);
Integer code = new Integer(c.hashCode());
if (c != null && hearingComponents.contains(code)) {
hearingComponents.remove(code);
}
}
}
/**
* Returns the {@link DragPolicy} which this manager obides by
*
* @return the <code>DragPolicy</code> for this manager. If no
* policy has been set the default policy is used.
*
* @see net.java.swingfx.jdraggable.DraggableManager#getDragPolicy()
* @see #setDragPolicy(DragPolicy)
* @see DragPolicy#DEFAULT
*/
@Override
public DragPolicy getDragPolicy() {
if (dragPolicy == null) {
setDragPolicy(DragPolicy.DEFAULT);
}
return dragPolicy;
}
/* (non-Javadoc)
* @see com.codecraig.jdraggable.DraggableManager#setDragPolicy(com.codecraig.jdraggable.DragPolicy)
*/
@Override
public void setDragPolicy(DragPolicy dragPolicy) {
this.dragPolicy = dragPolicy;
}
/* (non-Javadoc)
* @see java.awt.event.ContainerListener#componentAdded(java.awt.event.ContainerEvent)
*/
@Override
public void componentAdded(ContainerEvent e) {
if (dragListener == null || isDraggableContainerRegistered() == false) {
// this should not occur, since we listening to a container in the first place
throw new IllegalStateException("Draggable Container must be registered prior to adding components");
}
Component c = e.getChild();
Integer code = new Integer(c.hashCode());
if (hearingComponents.contains(code) == false) {
hearingComponents.add(code);
c.addMouseListener(dragListener);
c.addMouseMotionListener(dragListener);
}
}
/* (non-Javadoc)
* @see java.awt.event.ContainerListener#componentRemoved(java.awt.event.ContainerEvent)
*/
@Override
public void componentRemoved(ContainerEvent e) {
Component c = e.getChild();
Integer code = new Integer(c.hashCode());
if (hearingComponents.contains(code)) {
c.removeMouseListener(dragListener);
c.removeMouseMotionListener(dragListener);
hearingComponents.remove(code);
}
}
}