001 /* 002 * Copyright (c) 2005, romain guy (romain.guy@jext.org) and craig wickesser (craig@codecraig.com) 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 006 * 007 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 008 * * 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. 009 * * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 010 * 011 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 012 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 013 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 014 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 015 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 016 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 017 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 018 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 019 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 020 * POSSIBILITY OF SUCH DAMAGE. 021 */ 022 023 package net.java.swingfx.jdraggable; 024 025 import java.awt.Component; 026 import java.awt.Container; 027 import java.awt.event.ContainerEvent; 028 import java.util.HashSet; 029 import java.util.Set; 030 031 032 /** 033 * A default implementation of the {@link DraggableManager} interface. This 034 * implementation provides the basic functionality to enable {@link java.awt.Component} 035 * 's which implement the {@link Draggable} interface to actually be dragged 036 * when a user selects and drags a component. Depending on the {@link net.java.swingfx.jdraggable.DragPolicy} 037 * that is set, not all components must implement the <code>Draggable</code> interface. 038 * This implementation only supports one component to be dragged at a time. 039 * <br> 040 * <br> 041 * Another idea for an implementation of {@link DraggableManager} is one which 042 * registers a {@link javax.swing.JLayeredPane} as the "Draggable Container" 043 * such that when a component is dragged, it's depth (or Z-Order) within the 044 * "Draggable Container" is changed such that it is on top of the other components. 045 * 046 * @author craig 047 * @since v0.1 048 * <br> 049 * $Header: /cvs/swingfx/docs/api/src-html/net/java/swingfx/jdraggable/DefaultDraggableManager.html,v 1.1 2005/06/23 00:24:44 codecraig Exp $ 050 */ 051 public class DefaultDraggableManager implements DraggableManager { 052 /** 053 * the {@link Container} which contains {@link Component}'s, which may 054 * or may not, implement the {@link Draggable} interface 055 */ 056 private Container draggableContainer; 057 /** 058 * maintains whether a "Draggable Container" has been registered or not 059 */ 060 private boolean draggableContainerRegistered; 061 /** 062 * the component which was chosen to be dragged 063 */ 064 private Draggable hitDraggable; 065 /** 066 * maintains the state of <code>hitDraggable</cdoe> 067 */ 068 private byte draggableState; 069 /** 070 * the {@link DragPolicy} to obide by 071 */ 072 private DragPolicy dragPolicy; 073 /** 074 * the listener which provides the real ability for a component to 075 * change its location during a "drag" 076 */ 077 private DraggableListener dragListener; 078 /** 079 * maintains a {@link java.util.Set} of the components 080 * which have had a {@link DraggableListener} added to them. 081 * This is only used for "cleanup". 082 * This implementation stores the hash code's of each component. 083 */ 084 private Set hearingComponents; 085 /** 086 * determines whether the "draggable container" layout manager should be 087 * set to <code>null</code> once a component is dragged (this allows the components 088 * to maintain their position even if the container is resized), or not. 089 */ 090 private boolean nullifyLayout = true; 091 092 /** 093 * Creates a new {@link DraggableManager} with no "Draggable Container" 094 * registered 095 * 096 * @see #DefaultDraggableManager(Container) 097 * @see #registerDraggableContainer(Container) 098 */ 099 public DefaultDraggableManager() { 100 } 101 102 /** 103 * Creates a new {@link DraggableManager} and registers 104 * <code>draggableContainer</code> as the "Draggable Container" 105 * 106 * @param draggableContainer the "Draggable Container" to register 107 * 108 * @throws IllegalArgumentException if <code>draggableContainer</code> is 109 * <code>null</code> 110 * 111 * @see #DefaultDraggableManager() 112 * @see #registerDraggableContainer(Container) 113 */ 114 public DefaultDraggableManager(Container draggableContainer) { 115 if (draggableContainer == null) { 116 throw new IllegalArgumentException("Can not register a null Draggable Container"); 117 } 118 registerDraggableContainer(draggableContainer); 119 } 120 121 /* (non-Javadoc) 122 * @see com.codecraig.jdraggable.DraggableManager#setNullifyLayout(boolean) 123 */ 124 public void setNullifyLayout(boolean nullifyLayout) { 125 this.nullifyLayout = nullifyLayout; 126 } 127 128 /* (non-Javadoc) 129 * @see com.codecraig.jdraggable.DraggableManager#shouldNullifyLayout() 130 */ 131 public boolean shouldNullifyLayout() { 132 return nullifyLayout; 133 } 134 135 /* (non-Javadoc) 136 * @see com.codecraig.jdraggable.DraggableManager#startDrag(java.awt.Component) 137 */ 138 public boolean startDrag(Component componentToDrag) { 139 if (isDraggableContainerRegistered()) { 140 if (getDragPolicy().isDraggable(componentToDrag)) { 141 hitDraggable = (componentToDrag instanceof Draggable ? (Draggable) componentToDrag : new DraggableMask(componentToDrag)); 142 setState(STATE_STILL); 143 return true; 144 } 145 } 146 return false; 147 } 148 149 /* (non-Javadoc) 150 * @see com.codecraig.jdraggable.DraggableManager#dragging() 151 */ 152 public boolean dragging() { 153 if (isDraggableContainerRegistered()) { 154 if (hitDraggable != null) { 155 setState(STATE_DRAGGING); 156 return true; 157 } 158 } 159 return false; 160 } 161 162 /* (non-Javadoc) 163 * @see com.codecraig.jdraggable.DraggableManager#stopDrag() 164 */ 165 public boolean stopDrag() { 166 if (isDraggableContainerRegistered()) { 167 hitDraggable = null; 168 setState(STATE_UNKNOWN); 169 return true; 170 } 171 return false; 172 } 173 174 /** 175 * Returns the {@link Container} which registered itself as the 176 * "Draggable Container" with this {@link DraggableManager} 177 * 178 * @return the "Draggable Container" or <code>null</code> if not 179 * <code>Container</code> has been registered as the 180 * "Draggable Container" 181 */ 182 public Container getDraggableContainer() { 183 return draggableContainer; 184 } 185 186 private boolean isDraggableContainerRegistered() { 187 return draggableContainerRegistered; 188 } 189 190 /** 191 * Sets the state of <code>hitDraggable</code> 192 * 193 * @param state the state of <code>hitDraggable</code> 194 */ 195 private void setState(byte state) { 196 draggableState = state; 197 } 198 199 /** 200 * Returns the state of the current {@link Draggable} component which this 201 * manager is handling 202 * 203 * @return the state of the current <code>Draggable</code> component 204 * 205 * @see net.java.swingfx.jdraggable.DraggableManager#getState(net.java.swingfx.jdraggable.Draggable) 206 */ 207 public byte getState(Draggable draggableComponent) { 208 return draggableState; 209 } 210 211 /** 212 * Registers the given {@link Container} as the "Draggable Container" 213 * 214 * @param draggableContainer the <code>Container</code> whose <code>Draggable</code> 215 * components should be able to be dragged 216 * 217 * @see DraggableManager#registerDraggableContainer(java.awt.Container) 218 * 219 * @throws IllegalArgumentException if a <code>Container</code> has already 220 * been registered 221 */ 222 public void registerDraggableContainer(Container draggableContainer) { 223 if (this.draggableContainer == null) { 224 this.draggableContainer = draggableContainer; 225 draggableContainer.addContainerListener(this); 226 dragListener = new DraggableListener(this); 227 hearingComponents = new HashSet(); 228 draggableContainerRegistered = true; 229 } 230 else { 231 throw new IllegalArgumentException("A Draggable Container has already been registered"); 232 } 233 } 234 235 /** 236 * Un-Registers the given {@link Container} from being the "Draggable Container" 237 * 238 * @param draggableContainer the <code>Container</code> to unregister 239 * 240 * @see DraggableManager#unregisterDraggableContainer(Container) 241 * 242 * @throws IllegalArgumentException if the given container is not the same 243 * as the already registered container 244 * @throws IllegalStateException if no container is currently registered 245 */ 246 public void unregisterDraggableContainer(Container draggableContainer) { 247 if (this.draggableContainer == null) { 248 throw new IllegalStateException("Failed to unregister draggable container," + 249 " since no draggable container was registered"); 250 } 251 if (this.draggableContainer.equals(draggableContainer)) { 252 this.draggableContainer.removeContainerListener(this); 253 cleanupHearingComponents(); 254 this.dragListener = null; 255 this.draggableContainer = null; 256 draggableContainerRegistered = false; 257 } 258 else { 259 throw new IllegalArgumentException("Failed to unregister draggable container," + 260 " the given Container is not the same as the" + 261 " register draggable container"); 262 } 263 } 264 265 /** 266 * Removes the listeners from "hearing components" 267 */ 268 private void cleanupHearingComponents() { 269 int count = draggableContainer.getComponentCount(); 270 for (int i = count - 1; i >= 0 && hearingComponents.size() > 0; i--) { 271 Component c = draggableContainer.getComponent(i); 272 Integer code = new Integer(c.hashCode()); 273 if (c != null && hearingComponents.contains(code)) { 274 hearingComponents.remove(code); 275 } 276 } 277 } 278 279 /** 280 * Returns the {@link DragPolicy} which this manager obides by 281 * 282 * @return the <code>DragPolicy</code> for this manager. If no 283 * policy has been set the default policy is used. 284 * 285 * @see net.java.swingfx.jdraggable.DraggableManager#getDragPolicy() 286 * @see #setDragPolicy(DragPolicy) 287 * @see DragPolicy#DEFAULT 288 */ 289 public DragPolicy getDragPolicy() { 290 if (dragPolicy == null) { 291 setDragPolicy(DragPolicy.DEFAULT); 292 } 293 return dragPolicy; 294 } 295 296 /* (non-Javadoc) 297 * @see com.codecraig.jdraggable.DraggableManager#setDragPolicy(com.codecraig.jdraggable.DragPolicy) 298 */ 299 public void setDragPolicy(DragPolicy dragPolicy) { 300 this.dragPolicy = dragPolicy; 301 } 302 303 /* (non-Javadoc) 304 * @see java.awt.event.ContainerListener#componentAdded(java.awt.event.ContainerEvent) 305 */ 306 public void componentAdded(ContainerEvent e) { 307 if (dragListener == null || isDraggableContainerRegistered() == false) { 308 // this should not occur, since we listening to a container in the first place 309 throw new IllegalStateException("Draggable Container must be registered prior to adding components"); 310 } 311 Component c = e.getChild(); 312 Integer code = new Integer(c.hashCode()); 313 if (hearingComponents.contains(code) == false) { 314 hearingComponents.add(code); 315 c.addMouseListener(dragListener); 316 c.addMouseMotionListener(dragListener); 317 } 318 } 319 320 /* (non-Javadoc) 321 * @see java.awt.event.ContainerListener#componentRemoved(java.awt.event.ContainerEvent) 322 */ 323 public void componentRemoved(ContainerEvent e) { 324 Component c = e.getChild(); 325 Integer code = new Integer(c.hashCode()); 326 if (hearingComponents.contains(code)) { 327 hearingComponents.remove(code); 328 } 329 } 330 }