blob: fdf32b9adcba3e6a7d7cf52d11821121bfaf9af3 [file] [log] [blame]
/*
* 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<Editor> 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, false);
}
public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn) {
this(project, file, logicalLine, logicalColumn, -1, false);
}
public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file,
int logicalLine, int logicalColumn, boolean persistent) {
this(project, file, logicalLine, logicalColumn, -1, persistent);
}
public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file) {
this(project, file, -1, -1, -1, false);
}
private OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file,
int logicalLine, int logicalColumn, int offset, boolean persistent) {
myProject = project;
myFile = file;
myLogicalLine = logicalLine;
myLogicalColumn = logicalColumn;
myOffset = offset;
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<FileEditor> 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();
}
}
}