Merge branch 'appcode10' into merge_appcode10
[idea/community.git] / platform / platform-api / src / com / intellij / openapi / fileEditor / OpenFileDescriptor.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.fileEditor;
17
18 import com.intellij.ide.*;
19 import com.intellij.ide.FileEditorProvider;
20 import com.intellij.openapi.actionSystem.DataContext;
21 import com.intellij.openapi.actionSystem.DataKey;
22 import com.intellij.openapi.editor.*;
23 import com.intellij.openapi.fileTypes.FileType;
24 import com.intellij.openapi.fileTypes.FileTypeManager;
25 import com.intellij.openapi.fileTypes.INativeFileType;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.TextRange;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.pom.Navigatable;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import java.util.List;
34
35 public class OpenFileDescriptor implements Navigatable {
36   /**
37    * Tells descriptor to navigate in specific editor rather than file editor
38    * in main IDEA window.
39    * For example if you want to navigate in editor embedded into modal dialog,
40    * you should provide this data.
41    */
42   public static final DataKey<Editor> NAVIGATE_IN_EDITOR = DataKey.create("NAVIGATE_IN_EDITOR");
43
44   @NotNull
45   private final VirtualFile myFile;
46   private final int         myOffset;
47   private final int         myLogicalLine;
48   private final int         myLogicalColumn;
49   private final RangeMarker myRangeMarker;
50   private final Project     myProject;
51
52   private boolean myUseCurrentWindow = false;
53
54   public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int offset) {
55     this(project, file, -1, -1, offset, false);
56   }
57
58   public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file, int logicalLine, int logicalColumn) {
59     this(project, file, logicalLine, logicalColumn, -1, false);
60   }
61
62   public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file,
63                             int logicalLine, int logicalColumn, boolean persistent) {
64     this(project, file, logicalLine, logicalColumn, -1, persistent);
65   }
66
67   public OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file) {
68     this(project, file, -1, -1, -1, false);
69   }
70
71   private OpenFileDescriptor(@NotNull Project project, @NotNull VirtualFile file,
72                              int logicalLine, int logicalColumn, int offset, boolean persistent) {
73     myProject = project;
74
75     myFile = file;
76     myLogicalLine = logicalLine;
77     myLogicalColumn = logicalColumn;
78     myOffset = offset;
79     if (offset >= 0) {
80       myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, offset);
81     }
82     else if (logicalLine >= 0 ){
83       myRangeMarker = LazyRangeMarkerFactory.getInstance(project).createRangeMarker(file, logicalLine, Math.max(0, logicalColumn), persistent);
84     }
85     else {
86       myRangeMarker = null;
87     }
88   }
89
90   @NotNull
91   public VirtualFile getFile() {
92     return myFile;
93   }
94
95   @Nullable
96   public RangeMarker getRangeMarker() {
97     return myRangeMarker;
98   }
99
100   public int getOffset() {
101     return myRangeMarker != null && myRangeMarker.isValid() ? myRangeMarker.getStartOffset() : myOffset;
102   }
103
104   public int getLine() {
105     return myLogicalLine;
106   }
107
108   public int getColumn() {
109     return myLogicalColumn;
110   }
111
112   public void navigate(boolean requestFocus) {
113     if (!canNavigate()) {
114       throw new IllegalStateException("Navigation is not possible with null project");
115     }
116
117     if (!myFile.isDirectory() && navigateInEditor(myProject, requestFocus)) return;
118
119     navigateInProjectView();
120   }
121
122   private boolean navigateInEditor(Project project, boolean requestFocus) {
123     FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(myFile);
124     if (type == null || !myFile.isValid()) return false;
125
126     if (type instanceof INativeFileType && requestFocus) {
127       return ((INativeFileType) type).openFileInAssociatedApplication(project, myFile);
128     }
129
130     return navigateInRequestedEditor() || navigateInAnyFileEditor(project, requestFocus);
131   }
132
133   private boolean navigateInRequestedEditor() {
134     DataContext ctx = DataManager.getInstance().getDataContext();
135     Editor e = NAVIGATE_IN_EDITOR.getData(ctx);
136     if (e == null) return false;
137     if (FileDocumentManager.getInstance().getFile(e.getDocument()) != myFile) return false;
138     
139     navigateIn(e);
140     return true;
141   }
142
143   private boolean navigateInAnyFileEditor(Project project, boolean focusEditor) {
144     List<FileEditor> editors = FileEditorManager.getInstance(project).openEditor(this, focusEditor);
145     for (FileEditor editor : editors) {
146       if (editor instanceof TextEditor) {
147         Editor e = ((TextEditor)editor).getEditor();
148         unfoldCurrentLine(e);
149       }
150     }
151     return !editors.isEmpty();
152   }
153
154   private void navigateInProjectView() {
155     SelectInContext context = new SelectInContext() {
156       @NotNull
157       public Project getProject() {
158         return myProject;
159       }
160
161       @NotNull
162       public VirtualFile getVirtualFile() {
163         return myFile;
164       }
165
166       @Nullable
167       public Object getSelectorInFile() {
168         return null;
169       }
170
171       @Nullable
172       public FileEditorProvider getFileEditorProvider() {
173         return null;
174       }
175     };
176
177     for (SelectInTarget target : SelectInManager.getInstance(myProject).getTargets()) {
178       if (target.canSelect(context)) {
179         target.selectIn(context, true);
180         return;
181       }
182     }
183   }
184
185   public void navigateIn(@NotNull Editor e) {
186     final int offset = getOffset();
187     CaretModel caretModel = e.getCaretModel();
188     boolean caretMoved = false;
189     if (myLogicalLine >= 0) {
190       LogicalPosition pos = new LogicalPosition(myLogicalLine, Math.max(myLogicalColumn, 0));
191       if (offset < 0 || offset == e.logicalPositionToOffset(pos)) {
192         caretModel.moveToLogicalPosition(pos);
193         caretMoved = true;
194       }
195     }
196     if (!caretMoved && offset >= 0) {
197       caretModel.moveToOffset(Math.min(offset, e.getDocument().getTextLength()));
198       caretMoved = true;
199     }
200
201     if (caretMoved) {
202       e.getSelectionModel().removeSelection();
203       scrollToCaret(e);
204       unfoldCurrentLine(e);
205     }
206   }
207
208   private static void unfoldCurrentLine(@NotNull final Editor editor) {
209     final FoldRegion[] allRegions = editor.getFoldingModel().getAllFoldRegions();
210     final int offset = editor.getCaretModel().getOffset();
211     int line = editor.getDocument().getLineNumber(offset);
212     int start = editor.getDocument().getLineStartOffset(line);
213     int end = editor.getDocument().getLineEndOffset(line);
214     final TextRange range = new TextRange(start, end);
215     editor.getFoldingModel().runBatchFoldingOperation(new Runnable() {
216       public void run() {
217         for (FoldRegion region : allRegions) {
218           if (!region.isExpanded() && range.intersects(TextRange.create(region))) /*region.getStartOffset() <= offset && offset <= region.getEndOffset()*/ {
219             region.setExpanded(true);
220           }
221         }
222       }
223     });
224   }
225
226   private static void scrollToCaret(@NotNull Editor e) {
227     e.getScrollingModel().scrollToCaret(ScrollType.CENTER);
228   }
229
230   public boolean canNavigate() {
231     return myProject != null;
232   }
233
234   public boolean canNavigateToSource() {
235     return myProject != null;
236   }
237
238   public Project getProject() {
239     return myProject;
240   }
241
242   public OpenFileDescriptor setUseCurrentWindow(boolean search) {
243     myUseCurrentWindow = search;
244     return this;
245   }
246
247   public boolean isUseCurrentWindow() {
248     return myUseCurrentWindow;
249   }
250 }