| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.ui.tabs.impl; |
| |
| import com.intellij.ide.IdeBundle; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.ui.InplaceButton; |
| import com.intellij.ui.MouseDragHelper; |
| import com.intellij.ui.ScreenUtil; |
| import com.intellij.ui.awt.RelativePoint; |
| import com.intellij.ui.tabs.JBTabsPosition; |
| import com.intellij.ui.tabs.TabInfo; |
| import com.intellij.ui.util.Axis; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.event.MouseEvent; |
| |
| class DragHelper extends MouseDragHelper { |
| |
| private final JBTabsImpl myTabs; |
| private TabInfo myDragSource; |
| private Rectangle myDragOriginalRec; |
| |
| Rectangle myDragRec; |
| private Dimension myHoldDelta; |
| |
| private TabInfo myDragOutSource; |
| private TabLabel myPressedTabLabel; |
| |
| public DragHelper(JBTabsImpl tabs) { |
| super(tabs, tabs); |
| myTabs = tabs; |
| } |
| |
| |
| @Override |
| protected boolean isDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint) { |
| if (myDragSource == null || !myDragSource.canBeDraggedOut()) return false; |
| |
| TabLabel label = myTabs.myInfo2Label.get(myDragSource); |
| if (label == null) return false; |
| |
| int dX = dragToScreenPoint.x - startScreenPoint.x; |
| int dY = dragToScreenPoint.y - startScreenPoint.y; |
| boolean dragOut = |
| myTabs.getEffectiveLayout().isDragOut(label, dX, dY); |
| |
| return dragOut; |
| } |
| |
| @Override |
| protected void processDragOut(MouseEvent event, Point dragToScreenPoint, Point startScreenPoint, boolean justStarted) { |
| TabInfo.DragOutDelegate delegate = myDragOutSource.getDragOutDelegate(); |
| if (justStarted) { |
| delegate.dragOutStarted(event, myDragOutSource); |
| } else { |
| delegate.processDragOut(event, myDragOutSource); |
| } |
| event.consume(); |
| } |
| |
| @Override |
| protected void processDragOutFinish(MouseEvent event) { |
| super.processDragOutFinish(event); |
| |
| myDragOutSource.getDragOutDelegate().dragOutFinished(event, myDragOutSource); |
| } |
| |
| @Override |
| protected void processDragOutCancel() { |
| myDragOutSource.getDragOutDelegate().dragOutCancelled(myDragOutSource); |
| } |
| |
| @Override |
| protected void processMousePressed(MouseEvent event) { |
| // since selection change can cause tabs to be reordered, we need to remember the tab on which the mouse was pressed, otherwise |
| // we'll end up dragging the wrong tab (IDEA-65073) |
| myPressedTabLabel = findLabel(new RelativePoint(event).getPoint(myTabs)); |
| } |
| |
| protected void processDrag(MouseEvent event, Point targetScreenPoint, Point startPointScreen) { |
| if (!myTabs.isTabDraggingEnabled() || !isDragSource(event) || !MouseDragHelper.checkModifiers(event)) return; |
| |
| SwingUtilities.convertPointFromScreen(startPointScreen, myTabs); |
| |
| if (isDragJustStarted()) { |
| if (myPressedTabLabel == null) return; |
| |
| final Rectangle labelBounds = myPressedTabLabel.getBounds(); |
| |
| myHoldDelta = new Dimension(startPointScreen.x - labelBounds.x, startPointScreen.y - labelBounds.y); |
| myDragSource = myPressedTabLabel.getInfo(); |
| myDragRec = new Rectangle(startPointScreen, labelBounds.getSize()); |
| myDragOriginalRec = (Rectangle)myDragRec.clone(); |
| |
| myDragOriginalRec.x -= myHoldDelta.width; |
| myDragOriginalRec.y -= myHoldDelta.height; |
| } |
| else { |
| if (myDragRec == null) return; |
| |
| final Point toPoint = SwingUtilities.convertPoint(event.getComponent(), event.getPoint(), myTabs); |
| |
| myDragRec.x = toPoint.x; |
| myDragRec.y = toPoint.y; |
| } |
| |
| myDragRec.x -= myHoldDelta.width; |
| myDragRec.y -= myHoldDelta.height; |
| |
| final Rectangle headerRec = myTabs.getLastLayoutPass().getHeaderRectangle(); |
| ScreenUtil.moveToFit(myDragRec, headerRec, null); |
| |
| int deadZoneX = 0; |
| int deadZoneY = 0; |
| |
| final TabLabel top = findLabel(new Point(myDragRec.x + myDragRec.width / 2, myDragRec.y + deadZoneY)); |
| final TabLabel bottom = findLabel(new Point(myDragRec.x + myDragRec.width / 2, myDragRec.y + myDragRec.height - deadZoneY)); |
| final TabLabel left = findLabel(new Point(myDragRec.x + deadZoneX, myDragRec.y + myDragRec.height / 2)); |
| final TabLabel right = findLabel(new Point(myDragRec.x + myDragRec.width - deadZoneX, myDragRec.y + myDragRec.height / 2)); |
| |
| |
| TabLabel targetLabel; |
| if (myTabs.isHorizontalTabs()) { |
| targetLabel = findMostOverlapping(Axis.X, left, right); |
| if (targetLabel == null) { |
| targetLabel = findMostOverlapping(Axis.Y, top, bottom); |
| } |
| } else { |
| targetLabel = findMostOverlapping(Axis.Y, top, bottom); |
| if (targetLabel == null) { |
| targetLabel = findMostOverlapping(Axis.X, left, right); |
| } |
| } |
| |
| if (targetLabel != null) { |
| Rectangle saved = myDragRec; |
| myDragRec = null; |
| myTabs.reallocate(myDragSource, targetLabel.getInfo()); |
| myDragOriginalRec = myTabs.myInfo2Label.get(myDragSource).getBounds(); |
| myDragRec = saved; |
| myTabs.moveDraggedTabLabel(); |
| } else { |
| myTabs.moveDraggedTabLabel(); |
| final int border = myTabs.getTabsBorder().getTabBorderSize(); |
| headerRec.x -= border; |
| headerRec.y -= border; |
| headerRec.width += border * 2; |
| headerRec.height += border * 2; |
| myTabs.repaint(headerRec); |
| } |
| event.consume(); |
| } |
| |
| private boolean isDragSource(MouseEvent event) { |
| final Object source = event.getSource(); |
| if (source instanceof Component) { |
| return SwingUtilities.windowForComponent(myTabs) == SwingUtilities.windowForComponent((Component)source); |
| } |
| return false; |
| } |
| |
| private TabLabel findMostOverlapping(Axis measurer, TabLabel... labels) { |
| double freeSpace; |
| |
| if (measurer.getMinValue(myDragRec) < measurer.getMinValue(myDragOriginalRec)) { |
| freeSpace = measurer.getMaxValue(myDragOriginalRec) - measurer.getMaxValue(myDragRec); |
| } else { |
| freeSpace = measurer.getMinValue(myDragRec) - measurer.getMinValue(myDragOriginalRec); |
| } |
| |
| |
| int max = -1; |
| TabLabel maxLabel = null; |
| for (TabLabel each : labels) { |
| if (each == null) continue; |
| |
| final Rectangle eachBounds = each.getBounds(); |
| if (measurer.getSize(eachBounds) > freeSpace + freeSpace *0.3) continue; |
| |
| Rectangle intersection = myDragRec.intersection(eachBounds); |
| int size = intersection.width * intersection.height; |
| if (size > max) { |
| max = size; |
| maxLabel = each; |
| } |
| } |
| |
| return maxLabel; |
| } |
| |
| |
| @Nullable |
| private TabLabel findLabel(Point dragPoint) { |
| final Component at = myTabs.findComponentAt(dragPoint); |
| if (at instanceof InplaceButton) return null; |
| final TabLabel label = findLabel(at); |
| |
| return label != null && label.getParent() == myTabs && label.getInfo() != myDragSource ? label : null; |
| |
| } |
| |
| @Nullable |
| private TabLabel findLabel(Component c) { |
| Component eachParent = c; |
| while (eachParent != null && eachParent != myTabs) { |
| if (eachParent instanceof TabLabel) return (TabLabel)eachParent; |
| eachParent = eachParent.getParent(); |
| } |
| |
| return null; |
| } |
| |
| |
| @Override |
| protected boolean canStartDragging(JComponent dragComponent, Point dragComponentPoint) { |
| return findLabel(dragComponentPoint) != null; |
| } |
| |
| @Override |
| protected void processDragFinish(MouseEvent event, boolean willDragOutStart) { |
| super.processDragFinish(event, willDragOutStart); |
| |
| endDrag(willDragOutStart); |
| |
| final JBTabsPosition position = myTabs.getTabsPosition(); |
| |
| if (!willDragOutStart && myTabs.isAlphabeticalMode() && position != JBTabsPosition.top && position != JBTabsPosition.bottom) { |
| Point p = new Point(event.getPoint()); |
| p = SwingUtilities.convertPoint(event.getComponent(), p, myTabs); |
| if (myTabs.getVisibleRect().contains(p) && myPressedOnScreenPoint.distance(new RelativePoint(event).getScreenPoint()) > 15) { |
| final int answer = Messages.showOkCancelDialog(myTabs, |
| IdeBundle.message("alphabetical.mode.is.on.warning"), |
| IdeBundle.message("title.warning"), |
| Messages.getQuestionIcon()); |
| if (answer == Messages.OK) { |
| JBEditorTabs.setAlphabeticalMode(false); |
| myTabs.relayout(true, false); |
| myTabs.revalidate(); |
| } |
| } |
| } |
| } |
| |
| private void endDrag(boolean willDragOutStart) { |
| if (willDragOutStart) { |
| myDragOutSource = myDragSource; |
| } |
| |
| myDragSource = null; |
| myDragRec = null; |
| |
| myTabs.resetTabsCache(); |
| if (!willDragOutStart) { |
| myTabs.fireTabsMoved(); |
| } |
| myTabs.relayout(true, false); |
| |
| myTabs.revalidate(); |
| } |
| |
| @Override |
| protected void processDragCancel() { |
| endDrag(false); |
| } |
| |
| public TabInfo getDragSource() { |
| return myDragSource; |
| } |
| } |