2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ui;
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.ui.NullableComponent;
20 import com.intellij.openapi.util.Disposer;
21 import com.intellij.openapi.wm.IdeGlassPane;
22 import com.intellij.openapi.wm.IdeGlassPaneUtil;
23 import com.intellij.ui.awt.RelativePoint;
24 import com.intellij.util.ui.update.Activatable;
25 import com.intellij.util.ui.update.UiNotifyConnector;
29 import java.awt.event.MouseEvent;
30 import java.awt.event.MouseListener;
31 import java.awt.event.MouseMotionListener;
33 public abstract class MouseDragHelper implements MouseListener, MouseMotionListener {
35 public static final int DRAG_START_DEADZONE = 7;
37 private final JComponent myDragComponent;
39 private Point myPressPointScreen;
40 private Point myPressPointComponent;
42 private boolean myDraggingNow;
43 private boolean myDragJustStarted;
44 private IdeGlassPane myGlassPane;
45 private final Disposable myParentDisposable;
46 private Dimension myDelta;
48 private boolean myDetachPostponed;
49 private boolean myDetachingMode;
51 public MouseDragHelper(Disposable parent, final JComponent dragComponent) {
52 myDragComponent = dragComponent;
53 myParentDisposable = parent;
58 if (myGlassPane != null) return;
60 new UiNotifyConnector(myDragComponent, new Activatable() {
61 public void showNotify() {
65 public void hideNotify() {
70 Disposer.register(myParentDisposable, new Disposable() {
71 public void dispose() {
77 private void attach() {
78 if (myDetachPostponed) {
79 myDetachPostponed = false;
82 myGlassPane = IdeGlassPaneUtil.find(myDragComponent);
83 myGlassPane.addMousePreprocessor(this, myParentDisposable);
84 myGlassPane.addMouseMotionPreprocessor(this, myParentDisposable);
91 private void detach(boolean canPostponeDetach) {
92 if (canPostponeDetach && myDraggingNow) {
93 myDetachPostponed = true;
96 if (myGlassPane != null) {
97 myGlassPane.removeMousePreprocessor(this);
98 myGlassPane.removeMouseMotionPreprocessor(this);
103 public void mousePressed(final MouseEvent e) {
104 if (!canStartDragging(e)) return;
106 myPressPointScreen = new RelativePoint(e).getScreenPoint();
107 myPressPointComponent = e.getPoint();
109 myDelta = new Dimension();
110 if (myDragComponent.isShowing()) {
111 final Point delta = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), myDragComponent);
112 myDelta.width = delta.x;
113 myDelta.height = delta.y;
117 public void mouseReleased(final MouseEvent e) {
118 boolean wasDragging = myDraggingNow;
119 myPressPointScreen = null;
120 myDraggingNow = false;
121 myDragJustStarted = false;
125 if (myDetachingMode) {
126 processDragOutFinish(e);
128 processDragFinish(e, false);
132 myDraggingNow = false;
133 myPressPointComponent = null;
134 myPressPointScreen = null;
135 myDetachingMode = false;
137 if (myDetachPostponed) {
138 myDetachPostponed = false;
145 public void mouseDragged(final MouseEvent e) {
146 if (myPressPointScreen == null) return;
148 final boolean deadZone = isWithinDeadZone(e);
149 if (!myDraggingNow && !deadZone) {
150 myDraggingNow = true;
151 myDragJustStarted = true;
153 else if (myDraggingNow) {
154 myDragJustStarted = false;
157 if (myDraggingNow && myPressPointScreen != null) {
158 final Point draggedTo = new RelativePoint(e).getScreenPoint();
160 boolean dragOutStarted = false;
161 if (!myDetachingMode) {
162 if (isDragOut(e, draggedTo, (Point)myPressPointScreen.clone())) {
163 myDetachingMode = true;
164 processDragFinish(e, true);
165 dragOutStarted = true;
169 if (myDetachingMode) {
170 processDragOut(e, draggedTo, (Point)myPressPointScreen.clone(), dragOutStarted);
172 processDrag(e, draggedTo, (Point)myPressPointScreen.clone());
179 private boolean canStartDragging(MouseEvent me) {
180 if (me.getButton() != MouseEvent.BUTTON1) return false;
181 if (!myDragComponent.isShowing()) return false;
183 Component component = me.getComponent();
184 if (NullableComponent.Check.isNullOrHidden(component)) return false;
185 final Point dragComponentPoint = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), myDragComponent);
186 return canStartDragging(myDragComponent, dragComponentPoint);
189 protected boolean canStartDragging(final JComponent dragComponent, Point dragComponentPoint) {
194 protected void processDragFinish(final MouseEvent event, boolean willDragOutStart) {
197 protected void processDragOutFinish(final MouseEvent event) {
200 public final boolean isDragJustStarted() {
201 return myDragJustStarted;
204 protected abstract void processDrag(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint);
206 protected boolean isDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint) {
210 protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) {
214 private boolean isWithinDeadZone(final MouseEvent e) {
215 final Point screen = new RelativePoint(e).getScreenPoint();
216 return Math.abs(myPressPointScreen.x - screen.x - myDelta.width) < DRAG_START_DEADZONE &&
217 Math.abs(myPressPointScreen.y - screen.y - myDelta.height) < DRAG_START_DEADZONE;
220 public void mouseClicked(final MouseEvent e) {
223 public void mouseEntered(final MouseEvent e) {
226 public void mouseExited(final MouseEvent e) {
229 public void mouseMoved(final MouseEvent e) {