001 /* 002 * Created on Feb 1, 2008 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 * 014 * Copyright @2008-2010 the original author or authors. 015 */ 016 package org.fest.swing.core; 017 018 import static org.fest.swing.core.MouseButton.LEFT_BUTTON; 019 import static org.fest.swing.exception.ActionFailedException.actionFailure; 020 import static org.fest.swing.timing.Pause.pause; 021 import static org.fest.swing.util.Platform.isMacintosh; 022 import static org.fest.swing.util.Platform.isWindows; 023 import static org.fest.swing.util.TimeoutWatch.startWatchWithTimeoutOf; 024 025 import java.awt.*; 026 027 import org.fest.swing.annotation.RunsInEDT; 028 import org.fest.swing.exception.ActionFailedException; 029 import org.fest.swing.util.TimeoutWatch; 030 031 /** 032 * Understands <code>{@link Component}</code>-based drag and drop. 033 * @since 1.1 034 * 035 * @author Alex Ruiz 036 */ 037 public class ComponentDragAndDrop { 038 039 private final Robot robot; 040 041 /** 042 * Creates a new </code>{@link ComponentDragAndDrop}</code>. 043 * @param robot the robot to use to simulate user input. 044 */ 045 public ComponentDragAndDrop(Robot robot) { 046 this.robot = robot; 047 } 048 049 /** Number of pixels traversed before a drag starts. */ 050 public static final int DRAG_THRESHOLD = isWindows() || isMacintosh() ? 10 : 16; 051 052 /** 053 * Performs a drag action at the given point. 054 * @param target the target component. 055 * @param where the point where to start the drag action. 056 */ 057 @RunsInEDT 058 public void drag(Component target, Point where) { 059 robot.pressMouse(target, where, LEFT_BUTTON); 060 int dragDelay = settings().dragDelay(); 061 if (dragDelay > delayBetweenEvents()) pause(dragDelay); 062 mouseMove(target, where.x, where.y); 063 robot.waitForIdle(); 064 } 065 066 private void mouseMove(Component target, int x, int y) { 067 if (isWindows() || isMacintosh()) { 068 mouseMoveOnWindowsAndMacintosh(target, x, y); 069 return; 070 } 071 mouseMove(target, 072 point(x + DRAG_THRESHOLD / 2, y + DRAG_THRESHOLD / 2), 073 point(x + DRAG_THRESHOLD, y + DRAG_THRESHOLD), 074 point(x + DRAG_THRESHOLD / 2, y + DRAG_THRESHOLD / 2), 075 point(x, y) 076 ); 077 } 078 079 @RunsInEDT 080 private void mouseMoveOnWindowsAndMacintosh(Component target, int x, int y) { 081 Dimension size = target.getSize(); 082 int dx = distance(x, size.width); 083 int dy = distance(y, size.height); 084 if (dx == 0 && dy == 0) dx = DRAG_THRESHOLD; 085 mouseMove(target, 086 point(x + dx / 4, y + dy / 4), 087 point(x + dx / 2, y + dy / 2), 088 point(x + dx, y + dy), 089 point(x + dx + 1, y + dy) 090 ); 091 } 092 093 private int distance(int coordinate, int dimension) { 094 return coordinate + DRAG_THRESHOLD < dimension ? DRAG_THRESHOLD : 0; 095 } 096 097 private Point point(int x, int y) { return new Point(x, y); } 098 099 /** 100 * Ends a drag operation, releasing the mouse button over the given target location. 101 * <p> 102 * This method is tuned for native drag/drop operations, so if you get odd behavior, you might try using a simple 103 * <code>{@link Robot#moveMouse(Component, int, int)}</code> and <code>{@link Robot#releaseMouseButtons()}</code>. 104 * @param target the target component. 105 * @param where the point where the drag operation ends. 106 * @throws ActionFailedException if there is no drag action in effect. 107 */ 108 @RunsInEDT 109 public void drop(Component target, Point where) { 110 dragOver(target, where); 111 TimeoutWatch watch = startWatchWithTimeoutOf(settings().eventPostingDelay() * 4); 112 while (!robot.isDragging()) { 113 if (watch.isTimeOut()) throw actionFailure("There is no drag in effect"); 114 pause(); 115 } 116 int dropDelay = settings().dropDelay(); 117 int delayBetweenEvents = delayBetweenEvents(); 118 if (dropDelay > delayBetweenEvents) pause(dropDelay - delayBetweenEvents); 119 robot.releaseMouseButtons(); 120 robot.waitForIdle(); 121 } 122 123 private int delayBetweenEvents() { 124 return settings().delayBetweenEvents(); 125 } 126 127 private Settings settings() { 128 return robot.settings(); 129 } 130 131 /** 132 * Move the mouse appropriately to get from the source to the destination. Enter/exit events will be generated where 133 * appropriate. 134 * @param target the target component. 135 * @param where the point to drag over. 136 */ 137 public void dragOver(Component target, Point where) { 138 dragOver(target, where.x, where.y); 139 } 140 141 private void dragOver(Component target, int x, int y) { 142 robot.moveMouse(target, x - 4, y); 143 robot.moveMouse(target, x, y); 144 } 145 146 private void mouseMove(Component target, Point...points) { 147 for (Point p : points) robot.moveMouse(target, p.x, p.y); 148 } 149 }