/* * 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.openapi.fileEditor; import com.intellij.ide.*; import com.intellij.ide.FileEditorProvider; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.editor.*; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.INativeFileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.pom.Navigatable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; public class OpenFileDescriptor implements Navigatable { /** * Tells descriptor to navigate in specific editor rather than file editor * in main IDEA window. * For example if you want to navigate in editor embedded into modal dialog, * you should provide this data. */ public static final DataKey NAVIGATE_IN_EDITOR = DataKey.create("NAVIGATE_IN_EDITOR"); @NotNull private final VirtualFile myFile; private final int myOffset; private final int myLogicalLine; private final int myLogicalColumn; private final RangeMarker myRangeMarker; @NotNull private final Project myProject; private boolean myUseCurrentWindow = false; public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int offset) { this(project, file, -1, -1, offset, null, false); } public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn) { this(project, file, logicalLine, logicalColumn, -1, null, false); } public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn, boolean persistent) { this(project, file, logicalLine, logicalColumn, -1, null, persistent); } public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file) { this(project, file, -1, -1, -1, null, false); } private OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn, int offset, @Nullable RangeMarker rangeMarker, boolean persistent) { myProject = project; myFile = file; myLogicalLine = logicalLine; myLogicalColumn = logicalColumn; myOffset = offset; if (rangeMarker != null) { myRangeMarker = rangeMarker; } else if (offset >= 0) { myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, offset); } else if (logicalLine >= 0 ){ myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, logicalLine, Math.max(0, logicalColumn), persistent); } else { myRangeMarker = null; } } @NotNull public VirtualFile getFile() { return myFile; } @Nullable public RangeMarker getRangeMarker() { return myRangeMarker; } public int getOffset() { return myRangeMarker != null && myRangeMarker.isValid() ? myRangeMarker.getStartOffset() : myOffset; } public int getLine() { return myLogicalLine; } public int getColumn() { return myLogicalColumn; } @Override public void navigate(boolean requestFocus) { if (!canNavigate()) { throw new IllegalStateException("Navigation is not possible with null project"); } if (!myFile.isDirectory() && navigateInEditorOrNativeApp(myProject, requestFocus)) return; navigateInProjectView(requestFocus); } private boolean navigateInEditorOrNativeApp(@NotNull Project project, boolean requestFocus) { FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(myFile,project); if (type == null || !myFile.isValid()) return false; if (type instanceof INativeFileType) { return ((INativeFileType) type).openFileInAssociatedApplication(project, myFile); } return navigateInEditor(project, requestFocus); } public boolean navigateInEditor(@NotNull Project project, boolean requestFocus) { return navigateInRequestedEditor() || navigateInAnyFileEditor(project, requestFocus); } private boolean navigateInRequestedEditor() { DataContext ctx = DataManager.getInstance().getDataContext(); Editor e = NAVIGATE_IN_EDITOR.getData(ctx); if (e == null) return false; if (!Comparing.equal(FileDocumentManager.getInstance().getFile(e.getDocument()), myFile)) return false; navigateIn(e); return true; } private boolean navigateInAnyFileEditor(Project project, boolean focusEditor) { List editors = FileEditorManager.getInstance(project).openEditor(this, focusEditor); for (FileEditor editor : editors) { if (editor instanceof TextEditor) { Editor e = ((TextEditor)editor).getEditor(); unfoldCurrentLine(e); if (focusEditor) { IdeFocusManager.getInstance(myProject).requestFocus(e.getContentComponent(), true); } } } return !editors.isEmpty(); } private void navigateInProjectView(boolean requestFocus) { SelectInContext context = new SelectInContext() { @Override @NotNull public Project getProject() { return myProject; } @Override @NotNull public VirtualFile getVirtualFile() { return myFile; } @Override @Nullable public Object getSelectorInFile() { return null; } @Override @Nullable public FileEditorProvider getFileEditorProvider() { return null; } }; for (SelectInTarget target : SelectInManager.getInstance(myProject).getTargets()) { if (target.canSelect(context)) { target.selectIn(context, requestFocus); return; } } } public void navigateIn(@NotNull Editor e) { final int offset = getOffset(); CaretModel caretModel = e.getCaretModel(); boolean caretMoved = false; if (myLogicalLine >= 0) { LogicalPosition pos = new LogicalPosition(myLogicalLine, Math.max(myLogicalColumn, 0)); if (offset < 0 || offset == e.logicalPositionToOffset(pos)) { caretModel.removeSecondaryCarets(); caretModel.moveToLogicalPosition(pos); caretMoved = true; } } if (!caretMoved && offset >= 0) { caretModel.removeSecondaryCarets(); caretModel.moveToOffset(Math.min(offset, e.getDocument().getTextLength())); caretMoved = true; } if (caretMoved) { e.getSelectionModel().removeSelection(); scrollToCaret(e); unfoldCurrentLine(e); } } private static void unfoldCurrentLine(@NotNull final Editor editor) { final FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions(); final int offset = editor.getCaretModel().getOffset(); int line = editor.getDocument().getLineNumber(offset); int start = editor.getDocument().getLineStartOffset(line); int end = editor.getDocument().getLineEndOffset(line); final TextRange range = new TextRange(start, end); editor.getFoldingModel().runBatchFoldingOperation(new Runnable() { @Override public void run() { for (FoldRegion region : allRegions) { if (!region.isExpanded() && range.intersects(TextRange.create(region))) { region.setExpanded(true); } } } }); } private static void scrollToCaret(@NotNull Editor e) { e.getScrollingModel().scrollToCaret(ScrollType.CENTER); } @Override public boolean canNavigate() { return myFile.isValid(); } @Override public boolean canNavigateToSource() { return canNavigate(); } @NotNull public Project getProject() { return myProject; } public OpenFileDescriptor setUseCurrentWindow(boolean search) { myUseCurrentWindow = search; return this; } public boolean isUseCurrentWindow() { return myUseCurrentWindow; } public void dispose() { if (myRangeMarker != null) { myRangeMarker.dispose(); } } }