467666f31177ae055ff17ed2121deb0cffdae071
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / fileEditor / impl / text / TextEditorProvider.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.impl.text;
17
18 import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
19 import com.intellij.ide.structureView.StructureViewBuilder;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.editor.Document;
23 import com.intellij.openapi.editor.Editor;
24 import com.intellij.openapi.editor.LogicalPosition;
25 import com.intellij.openapi.editor.ScrollType;
26 import com.intellij.openapi.editor.ex.EditorEx;
27 import com.intellij.openapi.editor.ex.util.EditorUtil;
28 import com.intellij.openapi.fileEditor.*;
29 import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
30 import com.intellij.openapi.fileTypes.BinaryFileTypeDecompilers;
31 import com.intellij.openapi.fileTypes.FileType;
32 import com.intellij.openapi.project.DumbAware;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.project.ProjectManager;
35 import com.intellij.openapi.util.Disposer;
36 import com.intellij.openapi.util.Key;
37 import com.intellij.openapi.util.UserDataHolderBase;
38 import com.intellij.openapi.vfs.VirtualFile;
39 import com.intellij.pom.Navigatable;
40 import org.jdom.Element;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 import javax.swing.*;
46 import java.beans.PropertyChangeListener;
47
48 /**
49  * @author Anton Katilin
50  * @author Vladimir Kondratyev
51  */
52 public class TextEditorProvider implements FileEditorProvider, DumbAware {
53   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.text.TextEditorProvider");
54   private static final Key<TextEditor> TEXT_EDITOR_KEY = Key.create("textEditor");
55
56   @NonNls private static final String TYPE_ID = "text-editor";
57   @NonNls private static final String LINE_ATTR = "line";
58   @NonNls private static final String COLUMN_ATTR = "column";
59   @NonNls private static final String SELECTION_START_ATTR = "selection-start";
60   @NonNls private static final String SELECTION_END_ATTR = "selection-end";
61   @NonNls private static final String VERTICAL_SCROLL_PROPORTION_ATTR = "vertical-scroll-proportion";
62
63   public static TextEditorProvider getInstance() {
64     return ApplicationManager.getApplication().getComponent(TextEditorProvider.class);
65   }
66
67   public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
68     if (file.isDirectory() || !file.isValid()) {
69       return false;
70     }
71
72     final FileType ft = file.getFileType();
73     if (ft.isBinary()) {
74       return BinaryFileTypeDecompilers.INSTANCE.forFileType(ft) != null;
75     }
76
77     return true;
78   }
79
80   @NotNull
81   public FileEditor createEditor(@NotNull Project project, @NotNull final VirtualFile file) {
82     LOG.assertTrue(accept(project, file));
83     return new TextEditorImpl(project, file, this);
84   }
85
86   public void disposeEditor(@NotNull FileEditor editor) {
87     Disposer.dispose(editor);
88   }
89
90   @NotNull
91   public FileEditorState readState(@NotNull Element element, @NotNull Project project, @NotNull VirtualFile file) {
92     TextEditorState state = new TextEditorState();
93
94     try {
95       state.LINE = Integer.parseInt(element.getAttributeValue(LINE_ATTR));
96       state.COLUMN = Integer.parseInt(element.getAttributeValue(COLUMN_ATTR));
97       state.SELECTION_START = Integer.parseInt(element.getAttributeValue(SELECTION_START_ATTR));
98       state.SELECTION_END = Integer.parseInt(element.getAttributeValue(SELECTION_END_ATTR));
99       state.VERTICAL_SCROLL_PROPORTION = Float.parseFloat(element.getAttributeValue(VERTICAL_SCROLL_PROPORTION_ATTR));
100     }
101     catch (NumberFormatException ignored) {
102     }
103
104     return state;
105   }
106
107   public void writeState(@NotNull FileEditorState _state, @NotNull Project project, @NotNull Element element) {
108     TextEditorState state = (TextEditorState)_state;
109
110     element.setAttribute(LINE_ATTR, Integer.toString(state.LINE));
111     element.setAttribute(COLUMN_ATTR, Integer.toString(state.COLUMN));
112     element.setAttribute(SELECTION_START_ATTR, Integer.toString(state.SELECTION_START));
113     element.setAttribute(SELECTION_END_ATTR, Integer.toString(state.SELECTION_END));
114     element.setAttribute(VERTICAL_SCROLL_PROPORTION_ATTR, Float.toString(state.VERTICAL_SCROLL_PROPORTION));
115   }
116
117   @NotNull
118   public String getEditorTypeId() {
119     return TYPE_ID;
120   }
121
122   @NotNull
123   public FileEditorPolicy getPolicy() {
124     return FileEditorPolicy.NONE;
125   }
126
127   @NotNull public TextEditor getTextEditor(@NotNull Editor editor) {
128     TextEditor textEditor = editor.getUserData(TEXT_EDITOR_KEY);
129     if (textEditor == null) {
130       textEditor = createWrapperForEditor(editor);
131       putTextEditor(editor, textEditor);
132     }
133
134     return textEditor;
135   }
136
137   protected EditorWrapper createWrapperForEditor(final Editor editor) {
138     return new EditorWrapper(editor);
139   }
140
141   @Nullable
142   public static Document[] getDocuments(@NotNull FileEditor editor) {
143     if (editor instanceof DocumentsEditor) {
144       DocumentsEditor documentsEditor = (DocumentsEditor)editor;
145       Document[] documents = documentsEditor.getDocuments();
146       if (documents.length > 0) {
147         return documents;
148       }
149       else {
150         return null;
151       }
152     }
153
154     if (editor instanceof TextEditor) {
155       Document document = ((TextEditor)editor).getEditor().getDocument();
156       return new Document[]{document};
157     }
158
159     Project[] projects = ProjectManager.getInstance().getOpenProjects();
160     for (int i = projects.length - 1; i >= 0; i--) {
161       VirtualFile file = FileEditorManagerEx.getInstanceEx(projects[i]).getFile(editor);
162       if (file != null) {
163         Document document = FileDocumentManager.getInstance().getDocument(file);
164         if (document != null) {
165           return new Document[]{document};
166         }
167       }
168     }
169
170     return null;
171   }
172
173   static void putTextEditor(Editor editor, TextEditor textEditor) {
174     editor.putUserData(TEXT_EDITOR_KEY, textEditor);
175   }
176
177   protected TextEditorState getStateImpl(final Project project, final Editor editor, @NotNull FileEditorStateLevel level){
178     TextEditorState state = new TextEditorState();
179     state.LINE = editor.getCaretModel().getLogicalPosition().line;
180     state.COLUMN = editor.getCaretModel().getLogicalPosition().column;
181     state.SELECTION_START = editor.getSelectionModel().getSelectionStart();
182     state.SELECTION_END = editor.getSelectionModel().getSelectionEnd();
183
184     // Saving scrolling proportion on UNDO may cause undesirable results of undo action fails to perform since
185     // scrolling proportion restored sligtly differs from what have been saved.
186     state.VERTICAL_SCROLL_PROPORTION = level == FileEditorStateLevel.UNDO ? -1 : EditorUtil.calcVerticalScrollProportion(editor);
187     return state;
188   }
189
190   protected void setStateImpl(final Project project, final Editor editor, final TextEditorState state){
191     LogicalPosition pos = new LogicalPosition(state.LINE, state.COLUMN);
192     editor.getCaretModel().moveToLogicalPosition(pos);
193     editor.getSelectionModel().removeSelection();
194     editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
195
196     if (state.VERTICAL_SCROLL_PROPORTION != -1) {
197       EditorUtil.setVerticalScrollProportion(editor, state.VERTICAL_SCROLL_PROPORTION);
198     }
199
200     final Document document = editor.getDocument();
201
202     if (state.SELECTION_START == state.SELECTION_END) {
203       editor.getSelectionModel().removeSelection();
204     }
205     else {
206       int startOffset = Math.min(state.SELECTION_START, document.getTextLength());
207       int endOffset = Math.min(state.SELECTION_END, document.getTextLength());
208       editor.getSelectionModel().setSelection(startOffset, endOffset);
209     }
210     ((EditorEx) editor).stopOptimizedScrolling();
211     editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
212   }
213
214   protected class EditorWrapper extends UserDataHolderBase implements TextEditor {
215     private final Editor myEditor;
216
217     public EditorWrapper(Editor editor) {
218       myEditor = editor;
219     }
220
221     @NotNull
222     public Editor getEditor() {
223       return myEditor;
224     }
225
226     @NotNull
227     public JComponent getComponent() {
228       return myEditor.getComponent();
229     }
230
231     public JComponent getPreferredFocusedComponent() {
232       return myEditor.getContentComponent();
233     }
234
235     @NotNull
236     public String getName() {
237       return "Text";
238     }
239
240     public StructureViewBuilder getStructureViewBuilder() {
241       VirtualFile file = FileDocumentManager.getInstance().getFile(myEditor.getDocument());
242       if (file == null) return null;
243       
244       final Project project = myEditor.getProject();
245       LOG.assertTrue(project != null);
246       return StructureViewBuilder.PROVIDER.getStructureViewBuilder(file.getFileType(), file, project);
247     }
248
249     @NotNull
250     public FileEditorState getState(@NotNull FileEditorStateLevel level) {
251       return getStateImpl(null, myEditor, level);
252     }
253
254     public void setState(@NotNull FileEditorState state) {
255       setStateImpl(null, myEditor, (TextEditorState)state);
256     }
257
258     public boolean isModified() {
259       return false;
260     }
261
262     public boolean isValid() {
263       return true;
264     }
265
266     public void dispose() { }
267
268     public void selectNotify() { }
269
270     public void deselectNotify() { }
271
272     public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) { }
273
274     public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) { }
275
276     public BackgroundEditorHighlighter getBackgroundHighlighter() {
277       return null;
278     }
279
280     public FileEditorLocation getCurrentLocation() {
281       return null;
282     }
283
284     public boolean canNavigateTo(@NotNull final Navigatable navigatable) {
285       return false;
286     }
287
288     public void navigateTo(@NotNull final Navigatable navigatable) {
289     }
290   }
291 }