/* * Copyright 2000-2015 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.diff.tools.util.base; import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.util.*; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; import com.intellij.openapi.diff.DiffNavigationContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.annotations.RequiredDispatchThread; import java.awt.*; import java.util.List; public class InitialScrollPositionSupport { public abstract static class InitialScrollHelperBase { protected boolean myShouldScroll = true; @Nullable protected ScrollToPolicy myScrollToChange; @Nullable protected EditorsVisiblePositions myEditorsPosition; @Nullable protected LogicalPosition[] myCaretPosition; public void processContext(@NotNull DiffRequest request) { myScrollToChange = request.getUserData(DiffUserDataKeysEx.SCROLL_TO_CHANGE); myEditorsPosition = request.getUserData(EditorsVisiblePositions.KEY); myCaretPosition = request.getUserData(DiffUserDataKeysEx.EDITORS_CARET_POSITION); } public void updateContext(@NotNull DiffRequest request) { LogicalPosition[] carets = getCaretPositions(); EditorsVisiblePositions visiblePositions = getVisiblePositions(); request.putUserData(DiffUserDataKeysEx.SCROLL_TO_CHANGE, null); request.putUserData(EditorsVisiblePositions.KEY, visiblePositions); request.putUserData(DiffUserDataKeysEx.EDITORS_CARET_POSITION, carets); } @Nullable protected abstract LogicalPosition[] getCaretPositions(); @Nullable protected abstract EditorsVisiblePositions getVisiblePositions(); } private static abstract class SideInitialScrollHelper extends InitialScrollHelperBase { @Nullable @Override protected LogicalPosition[] getCaretPositions() { return doGetCaretPositions(getEditors()); } @Nullable @Override protected EditorsVisiblePositions getVisiblePositions() { return doGetVisiblePositions(getEditors()); } @RequiredDispatchThread protected boolean doScrollToPosition() { List<? extends Editor> editors = getEditors(); if (myCaretPosition == null || myCaretPosition.length != editors.size()) return false; doMoveCaretsToPositions(myCaretPosition, editors); try { disableSyncScroll(true); if (myEditorsPosition != null && myEditorsPosition.isSame(myCaretPosition)) { doScrollToVisiblePositions(myEditorsPosition, editors); } else { doScrollToCaret(editors); } } finally { disableSyncScroll(false); } return true; } @NotNull protected abstract List<? extends Editor> getEditors(); protected abstract void disableSyncScroll(boolean value); } public static abstract class TwosideInitialScrollHelper extends SideInitialScrollHelper { @Nullable protected Pair<Side, Integer> myScrollToLine; @Nullable protected DiffNavigationContext myNavigationContext; @Override public void processContext(@NotNull DiffRequest request) { super.processContext(request); myScrollToLine = request.getUserData(DiffUserDataKeys.SCROLL_TO_LINE); myNavigationContext = request.getUserData(DiffUserDataKeysEx.NAVIGATION_CONTEXT); } @Override public void updateContext(@NotNull DiffRequest request) { super.updateContext(request); request.putUserData(DiffUserDataKeys.SCROLL_TO_LINE, null); request.putUserData(DiffUserDataKeysEx.NAVIGATION_CONTEXT, null); } @RequiredDispatchThread public void onSlowRediff() { if (wasScrolled(getEditors())) myShouldScroll = false; if (myScrollToChange != null) return; if (myShouldScroll) myShouldScroll = !doScrollToLine(); if (myNavigationContext != null) return; if (myShouldScroll) myShouldScroll = !doScrollToPosition(); } @RequiredDispatchThread public void onRediff() { if (wasScrolled(getEditors())) myShouldScroll = false; if (myShouldScroll) myShouldScroll = !doScrollToChange(); if (myShouldScroll) myShouldScroll = !doScrollToLine(); if (myShouldScroll) myShouldScroll = !doScrollToContext(); if (myShouldScroll) myShouldScroll = !doScrollToPosition(); if (myShouldScroll) doScrollToFirstChange(); myShouldScroll = false; } @RequiredDispatchThread protected abstract boolean doScrollToChange(); @RequiredDispatchThread protected abstract boolean doScrollToFirstChange(); @RequiredDispatchThread protected abstract boolean doScrollToContext(); @RequiredDispatchThread protected abstract boolean doScrollToLine(); } public static abstract class ThreesideInitialScrollHelper extends SideInitialScrollHelper { @Nullable protected Pair<ThreeSide, Integer> myScrollToLine; @Override public void processContext(@NotNull DiffRequest request) { super.processContext(request); myScrollToLine = request.getUserData(DiffUserDataKeys.SCROLL_TO_LINE_THREESIDE); } @Override public void updateContext(@NotNull DiffRequest request) { super.updateContext(request); request.putUserData(DiffUserDataKeys.SCROLL_TO_LINE_THREESIDE, null); } public void onSlowRediff() { if (wasScrolled(getEditors())) myShouldScroll = false; if (myScrollToChange != null) return; if (myShouldScroll) myShouldScroll = !doScrollToLine(); if (myShouldScroll) myShouldScroll = !doScrollToPosition(); } public void onRediff() { if (wasScrolled(getEditors())) myShouldScroll = false; if (myShouldScroll) myShouldScroll = !doScrollToChange(); if (myShouldScroll) myShouldScroll = !doScrollToLine(); if (myShouldScroll) myShouldScroll = !doScrollToPosition(); if (myShouldScroll) doScrollToFirstChange(); myShouldScroll = false; } @RequiredDispatchThread protected abstract boolean doScrollToChange(); @RequiredDispatchThread protected abstract boolean doScrollToFirstChange(); @RequiredDispatchThread protected abstract boolean doScrollToLine(); @NotNull protected abstract List<? extends Editor> getEditors(); } @NotNull public static Point[] doGetScrollingPositions(@NotNull List<? extends Editor> editors) { Point[] carets = new Point[editors.size()]; for (int i = 0; i < editors.size(); i++) { carets[i] = DiffUtil.getScrollingPosition(editors.get(i)); } return carets; } @NotNull public static LogicalPosition[] doGetCaretPositions(@NotNull List<? extends Editor> editors) { LogicalPosition[] carets = new LogicalPosition[editors.size()]; for (int i = 0; i < editors.size(); i++) { carets[i] = DiffUtil.getCaretPosition(editors.get(i)); } return carets; } @Nullable public static EditorsVisiblePositions doGetVisiblePositions(@NotNull List<? extends Editor> editors) { LogicalPosition[] carets = doGetCaretPositions(editors); Point[] points = doGetScrollingPositions(editors); return new EditorsVisiblePositions(carets, points); } public static void doMoveCaretsToPositions(@NotNull LogicalPosition[] positions, @NotNull List<? extends Editor> editors) { for (int i = 0; i < editors.size(); i++) { Editor editor = editors.get(i); if (editor != null) editor.getCaretModel().moveToLogicalPosition(positions[i]); } } public static void doScrollToVisiblePositions(@NotNull EditorsVisiblePositions visiblePositions, @NotNull List<? extends Editor> editors) { for (int i = 0; i < editors.size(); i++) { Editor editor = editors.get(i); if (editor != null) DiffUtil.scrollToPoint(editor, visiblePositions.myPoints[i], false); } } public static void doScrollToCaret(@NotNull List<? extends Editor> editors) { for (int i = 0; i < editors.size(); i++) { Editor editor = editors.get(i); if (editor != null) DiffUtil.scrollToCaret(editor, false); } } public static boolean wasScrolled(@NotNull List<? extends Editor> editors) { for (Editor editor : editors) { if (editor == null) continue; if (editor.getCaretModel().getOffset() != 0) return true; if (editor.getScrollingModel().getVerticalScrollOffset() != 0) return true; if (editor.getScrollingModel().getHorizontalScrollOffset() != 0) return true; } return false; } public static class EditorsVisiblePositions { public static final Key<EditorsVisiblePositions> KEY = Key.create("Diff.EditorsVisiblePositions"); @NotNull public final LogicalPosition[] myCaretPosition; @NotNull public final Point[] myPoints; public EditorsVisiblePositions(@NotNull LogicalPosition caretPosition, @NotNull Point points) { this(new LogicalPosition[]{caretPosition}, new Point[]{points}); } public EditorsVisiblePositions(@NotNull LogicalPosition[] caretPosition, @NotNull Point[] points) { myCaretPosition = caretPosition; myPoints = points; } public boolean isSame(@Nullable LogicalPosition... caretPosition) { // TODO: allow small fluctuations ? if (caretPosition == null) return true; if (myCaretPosition.length != caretPosition.length) return false; for (int i = 0; i < caretPosition.length; i++) { if (!caretPosition[i].equals(myCaretPosition[i])) return false; } return true; } } }