A massive organize imports and formatting of the sources using default Eclipse code...
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpeclipse / ui / editor / StructuredTextEditor.java
1 /*
2  * Copyright (c) 2004 Christopher Lenz and others
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     Christopher Lenz - initial API and implementation
10  * 
11  * $Id: StructuredTextEditor.java,v 1.2 2006-10-21 23:13:54 pombredanne Exp $
12  */
13
14 package net.sourceforge.phpeclipse.ui.editor;
15
16 import net.sourceforge.phpeclipse.core.model.ISourceModel;
17 import net.sourceforge.phpeclipse.core.model.ISourceReference;
18 import net.sourceforge.phpeclipse.ui.text.IReconcilingParticipant;
19 import net.sourceforge.phpeclipse.ui.views.outline.ModelBasedOutlinePage;
20
21 import org.eclipse.jface.preference.IPreferenceStore;
22 import org.eclipse.jface.text.IRegion;
23 import org.eclipse.jface.text.source.ISourceViewer;
24 import org.eclipse.jface.viewers.ISelectionChangedListener;
25 import org.eclipse.jface.viewers.IStructuredSelection;
26 import org.eclipse.jface.viewers.SelectionChangedEvent;
27 import org.eclipse.swt.custom.StyledText;
28 import org.eclipse.swt.widgets.Shell;
29 import org.eclipse.ui.editors.text.TextEditor;
30 import org.eclipse.ui.texteditor.IUpdate;
31 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
32
33 /**
34  * Abstract base class for editors that keep a source model synchronized with
35  * the textual contants being edited.
36  */
37 public abstract class StructuredTextEditor extends TextEditor implements
38                 IReconcilingParticipant {
39
40         // Inner Classes -----------------------------------------------------------
41
42         /**
43          * Listens to changes to the selection in the outline page, and changes the
44          * selection and highlight range in the editor accordingly.
45          */
46         private class OutlineSelectionChangedListener implements
47                         ISelectionChangedListener {
48
49                 /*
50                  * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
51                  */
52                 public void selectionChanged(SelectionChangedEvent event) {
53                         IStructuredSelection selection = (IStructuredSelection) event
54                                         .getSelection();
55                         if (selection.isEmpty()) {
56                                 resetHighlightRange();
57                         } else {
58                                 ISourceReference element = (ISourceReference) selection
59                                                 .getFirstElement();
60                                 highlightElement(element, true);
61                         }
62                 }
63
64         }
65
66         // Instance Variables ------------------------------------------------------
67
68         /**
69          * The associated outline page.
70          */
71         private IContentOutlinePage outlinePage;
72
73         /**
74          * Listens to changes in the outline page's selection to update the editor
75          * selection and highlight range.
76          */
77         private ISelectionChangedListener outlinePageSelectionListener;
78
79         // TextEditor Implementation -----------------------------------------------
80
81         /*
82          * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
83          */
84         public Object getAdapter(Class adapter) {
85                 if (adapter.equals(IContentOutlinePage.class)) {
86                         if (outlinePage == null) {
87                                 outlinePage = createOutlinePage();
88                                 outlinePageSelectionListener = new OutlineSelectionChangedListener();
89                                 outlinePage
90                                                 .addSelectionChangedListener(outlinePageSelectionListener);
91                         }
92                         return outlinePage;
93                 }
94                 return super.getAdapter(adapter);
95         }
96
97         /*
98          * @see org.eclipse.ui.texteditor.AbstractTextEditor#handleCursorPositionChanged()
99          */
100         protected void handleCursorPositionChanged() {
101                 super.handleCursorPositionChanged();
102                 highlightElement(computeHighlightRangeSourceReference(), false);
103                 synchronizeOutlinePageSelection();
104         }
105
106         // IReconcilingParticipant Implementation ----------------------------------
107
108         /*
109          * @see IReconcilingParticipant#reconciled()
110          */
111         public void reconciled() {
112                 Shell shell = getSite().getShell();
113                 if ((shell != null) && !shell.isDisposed()) {
114                         shell.getDisplay().asyncExec(new Runnable() {
115                                 public void run() {
116                                         if (outlinePage instanceof IUpdate) {
117                                                 ((IUpdate) outlinePage).update();
118                                         }
119                                         synchronizeOutlinePageSelection();
120                                 }
121                         });
122                 }
123         }
124
125         // Public Methods ----------------------------------------------------------
126
127         /**
128          * Computes and returns the source reference that includes the caret and
129          * serves as provider for the outline page selection and the editor range
130          * indication.
131          * 
132          * @return the computed source reference
133          */
134         public ISourceReference computeHighlightRangeSourceReference() {
135                 ISourceViewer sourceViewer = getSourceViewer();
136                 if (sourceViewer == null) {
137                         return null;
138                 }
139                 StyledText styledText = sourceViewer.getTextWidget();
140                 if ((styledText == null) || styledText.isDisposed()) {
141                         return null;
142                 }
143                 int offset = sourceViewer.getVisibleRegion().getOffset();
144                 int caret = offset + styledText.getCaretOffset();
145
146                 return getElementAt(caret);
147         }
148
149         /**
150          * Returns the source model element at the specified offset.
151          * 
152          * @param offset
153          *            the offset into the document
154          * @return the element at the given offset, or <tt>null</tt> if no model
155          *         is available or there is no element at the offset
156          */
157         public ISourceReference getElementAt(int offset) {
158                 ISourceReference retVal = null;
159                 ISourceModel model = getSourceModel();
160                 if (model != null) {
161                         ISourceReference elements[] = model.getElements();
162                         retVal = getElementAt(model, elements, offset);
163                 }
164                 return retVal;
165         }
166
167         /**
168          * Returns the structure source model corresponding to the document
169          * currently being edited.
170          * 
171          * Concrete implementations must implement this method to return the model
172          * appropriate to the content being edited.
173          * 
174          * @return the source model
175          */
176         public abstract ISourceModel getSourceModel();
177
178         /**
179          * Informs the editor that its outliner has been closed.
180          * 
181          * TODO There must be a more elegant way to get notified when the outline
182          * page was closed. Otherwise move this method into an interface
183          */
184         public void outlinePageClosed() {
185                 if (outlinePage != null) {
186                         outlinePage
187                                         .removeSelectionChangedListener(outlinePageSelectionListener);
188                         outlinePage = null;
189                         resetHighlightRange();
190                 }
191         }
192
193         /**
194          * Synchronizes the outliner selection with the given element position in
195          * the editor.
196          * 
197          * @param element
198          *            the java element to select
199          */
200         public void synchronizeOutlinePage(ISourceReference element) {
201                 if (outlinePage != null) {
202                         outlinePage
203                                         .removeSelectionChangedListener(outlinePageSelectionListener);
204                         if (outlinePage instanceof ModelBasedOutlinePage) {
205                                 ((ModelBasedOutlinePage) outlinePage).select(element);
206                         }
207                         outlinePage
208                                         .addSelectionChangedListener(outlinePageSelectionListener);
209                 }
210         }
211
212         /**
213          * Synchronizes the outliner selection with the currently highlighted source
214          * reference.
215          */
216         public void synchronizeOutlinePage() {
217                 ISourceReference element = computeHighlightRangeSourceReference();
218                 synchronizeOutlinePage(element);
219         }
220
221         // Protected Methods -------------------------------------------------------
222
223         protected abstract IContentOutlinePage createOutlinePage();
224
225         /**
226          * Highlights the given element.
227          * 
228          * @param element
229          *            the element that should be highlighted
230          * @param moveCursor
231          *            whether the cursor should be moved to the element
232          */
233         protected final void highlightElement(ISourceReference element,
234                         boolean moveCursor) {
235                 if (element != null) {
236                         IRegion highlightRegion = element.getSourceRegion();
237                         setHighlightRange(highlightRegion.getOffset(), highlightRegion
238                                         .getLength(), moveCursor);
239                 } else {
240                         resetHighlightRange();
241                 }
242         }
243
244         /**
245          * Returns whether the outline page is currently linked with the editor,
246          * meaning that its selection should automatically be updated to reflect the
247          * current cursor position.
248          * 
249          * @return <tt>true</tt> if the outline page is linked with the editor,
250          *         <tt>false</tt> otherwise
251          */
252         protected abstract boolean isOutlineLinkedWithEditor();
253
254         // Private Methods ---------------------------------------------------------
255
256         /**
257          * Recursively searches the specified list of elements managed by the given
258          * model for the element that covers the specified offfset with minimal
259          * padding.
260          * 
261          * @param model
262          *            the source model
263          * @param elements
264          *            the current list of elements
265          * @param offset
266          *            the offset into the document
267          * @return the model element at the specified offset, or <tt>null</tt> if
268          *         no element could be found
269          */
270         private static ISourceReference getElementAt(ISourceModel model,
271                         ISourceReference elements[], int offset) {
272                 ISourceReference retVal = null;
273                 for (int i = 0; i < elements.length; i++) {
274                         ISourceReference element = elements[i];
275                         IRegion region = element.getSourceRegion();
276                         if ((offset > region.getOffset())
277                                         && (offset < (region.getOffset() + region.getLength()))) {
278                                 ISourceReference[] children = model.getChildren(element);
279                                 if (children.length > 0) {
280                                         retVal = getElementAt(model, children, offset);
281                                         if (retVal != null) {
282                                                 break;
283                                         }
284                                 }
285                                 if (retVal == null) {
286                                         retVal = element;
287                                 }
288                         }
289                 }
290                 return retVal;
291         }
292
293         private void synchronizeOutlinePageSelection() {
294                 IPreferenceStore store = getPreferenceStore();
295                 if (store != null) {
296                         boolean linkWithEditor = isOutlineLinkedWithEditor();
297                         if (linkWithEditor) {
298                                 synchronizeOutlinePage(computeHighlightRangeSourceReference());
299                         }
300                 }
301         }
302
303 }