1 package com.hammurapi.jcapture; 2 3 import java.awt.Component; 4 import java.awt.Cursor; 5 import java.awt.Dimension; 6 import java.awt.GraphicsEnvironment; 7 import java.awt.Insets; 8 import java.awt.Point; 9 import java.awt.Rectangle; 10 import java.awt.Window; 11 import java.awt.event.MouseAdapter; 12 import java.awt.event.MouseEvent; 13 14 import javax.swing.JComponent; 15 import javax.swing.SwingUtilities; 16 17 /** 18 * This class allows you to move a Component by using a mouse. The Component 19 * moved can be a high level Window (ie. Window, Frame, Dialog) in which case 20 * the Window is moved within the desktop. Or the Component can belong to a 21 * Container in which case the Component is moved within the Container. 22 * 23 * When moving a Window, the listener can be added to a child Component of the 24 * Window. In this case attempting to move the child will result in the Window 25 * moving. For example, you might create a custom "Title Bar" for an undecorated 26 * Window and moving of the Window is accomplished by moving the title bar only. 27 * Multiple components can be registered as "window movers". 28 * 29 * Components can be registered when the class is created. Additional components 30 * can be added at any time using the registerComponent() method. 31 * 32 * Taken from http://tips4java.wordpress.com/2009/06/14/moving-windows/ 33 */ 34 public class ComponentMover extends MouseAdapter { 35 private Insets dragInsets = new Insets(0, 0, 0, 0); 36 private Dimension snapSize = new Dimension(1, 1); 37 private Insets edgeInsets = new Insets(0, 0, 0, 0); 38 private boolean changeCursor = true; 39 private boolean autoLayout = false; 40 41 private Class destinationClass; 42 private Component destinationComponent; 43 private Component destination; 44 private Component source; 45 46 private Point pressed; 47 private Point location; 48 49 private Cursor originalCursor; 50 private boolean autoscrolls; 51 private boolean potentialDrag; 52 53 /** 54 * Constructor for moving individual components. The components must be 55 * regisetered using the registerComponent() method. 56 */ ComponentMover()57 public ComponentMover() { 58 } 59 60 /** 61 * Constructor to specify a Class of Component that will be moved when drag 62 * events are generated on a registered child component. The events will be 63 * passed to the first ancestor of this specified class. 64 * 65 * @param destinationClass 66 * the Class of the ancestor component 67 * @param component 68 * the Components to be registered for forwarding drag events to 69 * the ancestor Component. 70 */ ComponentMover(Class destinationClass, Component... components)71 public ComponentMover(Class destinationClass, Component... components) { 72 this.destinationClass = destinationClass; 73 registerComponent(components); 74 } 75 76 /** 77 * Constructor to specify a parent component that will be moved when drag 78 * events are generated on a registered child component. 79 * 80 * @param destinationComponent 81 * the component drage events should be forwareded to 82 * @param components 83 * the Components to be registered for forwarding drag events to 84 * the parent component to be moved 85 */ ComponentMover(Component destinationComponent, Component... components)86 public ComponentMover(Component destinationComponent, 87 Component... components) { 88 this.destinationComponent = destinationComponent; 89 registerComponent(components); 90 } 91 92 /** 93 * Get the auto layout property 94 * 95 * @return the auto layout property 96 */ isAutoLayout()97 public boolean isAutoLayout() { 98 return autoLayout; 99 } 100 101 /** 102 * Set the auto layout property 103 * 104 * @param autoLayout 105 * when true layout will be invoked on the parent container 106 */ setAutoLayout(boolean autoLayout)107 public void setAutoLayout(boolean autoLayout) { 108 this.autoLayout = autoLayout; 109 } 110 111 /** 112 * Get the change cursor property 113 * 114 * @return the change cursor property 115 */ isChangeCursor()116 public boolean isChangeCursor() { 117 return changeCursor; 118 } 119 120 /** 121 * Set the change cursor property 122 * 123 * @param changeCursor 124 * when true the cursor will be changed to the Cursor.MOVE_CURSOR 125 * while the mouse is pressed 126 */ setChangeCursor(boolean changeCursor)127 public void setChangeCursor(boolean changeCursor) { 128 this.changeCursor = changeCursor; 129 } 130 131 /** 132 * Get the drag insets 133 * 134 * @return the drag insets 135 */ getDragInsets()136 public Insets getDragInsets() { 137 return dragInsets; 138 } 139 140 /** 141 * Set the drag insets. The insets specify an area where mouseDragged events 142 * should be ignored and therefore the component will not be moved. This 143 * will prevent these events from being confused with a MouseMotionListener 144 * that supports component resizing. 145 * 146 * @param dragInsets 147 */ setDragInsets(Insets dragInsets)148 public void setDragInsets(Insets dragInsets) { 149 this.dragInsets = dragInsets; 150 } 151 152 /** 153 * Get the bounds insets 154 * 155 * @return the bounds insets 156 */ getEdgeInsets()157 public Insets getEdgeInsets() { 158 return edgeInsets; 159 } 160 161 /** 162 * Set the edge insets. The insets specify how close to each edge of the 163 * parent component that the child component can be moved. Positive values 164 * means the component must be contained within the parent. Negative values 165 * means the component can be moved outside the parent. 166 * 167 * @param edgeInsets 168 */ setEdgeInsets(Insets edgeInsets)169 public void setEdgeInsets(Insets edgeInsets) { 170 this.edgeInsets = edgeInsets; 171 } 172 173 /** 174 * Remove listeners from the specified component 175 * 176 * @param component 177 * the component the listeners are removed from 178 */ deregisterComponent(Component... components)179 public void deregisterComponent(Component... components) { 180 for (Component component : components) 181 component.removeMouseListener(this); 182 } 183 184 /** 185 * Add the required listeners to the specified component 186 * 187 * @param component 188 * the component the listeners are added to 189 */ registerComponent(Component... components)190 public void registerComponent(Component... components) { 191 for (Component component : components) 192 component.addMouseListener(this); 193 } 194 195 /** 196 * Get the snap size 197 * 198 * @return the snap size 199 */ getSnapSize()200 public Dimension getSnapSize() { 201 return snapSize; 202 } 203 204 /** 205 * Set the snap size. Forces the component to be snapped to the closest grid 206 * position. Snapping will occur when the mouse is dragged half way. 207 */ setSnapSize(Dimension snapSize)208 public void setSnapSize(Dimension snapSize) { 209 if (snapSize.width < 1 || snapSize.height < 1) 210 throw new IllegalArgumentException( 211 "Snap sizes must be greater than 0"); 212 213 this.snapSize = snapSize; 214 } 215 216 /** 217 * Setup the variables used to control the moving of the component: 218 * 219 * source - the source component of the mouse event destination - the 220 * component that will ultimately be moved pressed - the Point where the 221 * mouse was pressed in the destination component coordinates. 222 */ 223 @Override mousePressed(MouseEvent e)224 public void mousePressed(MouseEvent e) { 225 source = e.getComponent(); 226 int width = source.getSize().width - dragInsets.left - dragInsets.right; 227 int height = source.getSize().height - dragInsets.top 228 - dragInsets.bottom; 229 Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width, 230 height); 231 232 if (r.contains(e.getPoint())) 233 setupForDragging(e); 234 } 235 setupForDragging(MouseEvent e)236 private void setupForDragging(MouseEvent e) { 237 source.addMouseMotionListener(this); 238 potentialDrag = true; 239 240 // Determine the component that will ultimately be moved 241 242 if (destinationComponent != null) { 243 destination = destinationComponent; 244 } else if (destinationClass == null) { 245 destination = source; 246 } else // forward events to destination component 247 { 248 destination = SwingUtilities.getAncestorOfClass(destinationClass, 249 source); 250 } 251 252 pressed = e.getLocationOnScreen(); 253 location = destination.getLocation(); 254 255 if (changeCursor) { 256 originalCursor = source.getCursor(); 257 source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 258 } 259 260 // Making sure autoscrolls is false will allow for smoother dragging of 261 // individual components 262 263 if (destination instanceof JComponent) { 264 JComponent jc = (JComponent) destination; 265 autoscrolls = jc.getAutoscrolls(); 266 jc.setAutoscrolls(false); 267 } 268 } 269 270 /** 271 * Move the component to its new location. The dragged Point must be in the 272 * destination coordinates. 273 */ 274 @Override mouseDragged(MouseEvent e)275 public void mouseDragged(MouseEvent e) { 276 Point dragged = e.getLocationOnScreen(); 277 int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width); 278 int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height); 279 280 int locationX = location.x + dragX; 281 int locationY = location.y + dragY; 282 283 // Mouse dragged events are not generated for every pixel the mouse 284 // is moved. Adjust the location to make sure we are still on a 285 // snap value. 286 287 // while (locationX < edgeInsets.left) 288 // locationX += snapSize.width; 289 // 290 // while (locationY < edgeInsets.top) 291 // locationY += snapSize.height; 292 // 293 // Dimension d = getBoundingSize(destination); 294 295 // while (locationX + destination.getSize().width + edgeInsets.right > d.width) 296 // locationX -= snapSize.width; 297 // 298 // while (locationY + destination.getSize().height + edgeInsets.bottom > d.height) 299 // locationY -= snapSize.height; 300 301 // Adjustments are finished, move the component 302 303 destination.setLocation(locationX, locationY); 304 } 305 306 /* 307 * Determine how far the mouse has moved from where dragging started (Assume 308 * drag direction is down and right for positive drag distance) 309 */ getDragDistance(int larger, int smaller, int snapSize)310 private int getDragDistance(int larger, int smaller, int snapSize) { 311 int halfway = snapSize / 2; 312 int drag = larger - smaller; 313 drag += (drag < 0) ? -halfway : halfway; 314 drag = (drag / snapSize) * snapSize; 315 316 return drag; 317 } 318 319 /* 320 * Get the bounds of the parent of the dragged component. 321 */ getBoundingSize(Component source)322 private Dimension getBoundingSize(Component source) { 323 if (source instanceof Window) { 324 GraphicsEnvironment env = GraphicsEnvironment 325 .getLocalGraphicsEnvironment(); 326 Rectangle bounds = env.getMaximumWindowBounds(); 327 return new Dimension(bounds.width, bounds.height); 328 } else { 329 return source.getParent().getSize(); 330 } 331 } 332 333 /** 334 * Restore the original state of the Component 335 */ 336 @Override mouseReleased(MouseEvent e)337 public void mouseReleased(MouseEvent e) { 338 if (!potentialDrag) 339 return; 340 341 source.removeMouseMotionListener(this); 342 potentialDrag = false; 343 344 if (changeCursor) 345 source.setCursor(originalCursor); 346 347 if (destination instanceof JComponent) { 348 ((JComponent) destination).setAutoscrolls(autoscrolls); 349 } 350 351 // Layout the components on the parent container 352 353 if (autoLayout) { 354 if (destination instanceof JComponent) { 355 ((JComponent) destination).revalidate(); 356 } else { 357 destination.validate(); 358 } 359 } 360 } 361 } 362