com.intellij.ui.tabs.impl.DragHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ui.tabs.impl.DragHelper.java

Source

/*
 * 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;
    }
}