KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > texteditor > AbstractTextEditor


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
11  * Michel Ishizuka (cqw10305@nifty.com) - http://bugs.eclipse.org/bugs/show_bug.cgi?id=68963
12  * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668
13  * Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573
14  *******************************************************************************/

15 package org.eclipse.ui.texteditor;
16
17
18 import java.lang.reflect.InvocationTargetException JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.ResourceBundle JavaDoc;
25
26 import org.osgi.framework.Bundle;
27
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.custom.BusyIndicator;
30 import org.eclipse.swt.custom.ST;
31 import org.eclipse.swt.custom.StyledText;
32 import org.eclipse.swt.custom.VerifyKeyListener;
33 import org.eclipse.swt.dnd.DND;
34 import org.eclipse.swt.dnd.DragSource;
35 import org.eclipse.swt.dnd.DragSourceAdapter;
36 import org.eclipse.swt.dnd.DragSourceEvent;
37 import org.eclipse.swt.dnd.DropTargetAdapter;
38 import org.eclipse.swt.dnd.DropTargetEvent;
39 import org.eclipse.swt.dnd.DropTargetListener;
40 import org.eclipse.swt.dnd.TextTransfer;
41 import org.eclipse.swt.dnd.Transfer;
42 import org.eclipse.swt.events.KeyEvent;
43 import org.eclipse.swt.events.KeyListener;
44 import org.eclipse.swt.events.MouseEvent;
45 import org.eclipse.swt.events.MouseListener;
46 import org.eclipse.swt.events.VerifyEvent;
47 import org.eclipse.swt.events.VerifyListener;
48 import org.eclipse.swt.graphics.Color;
49 import org.eclipse.swt.graphics.Font;
50 import org.eclipse.swt.graphics.FontData;
51 import org.eclipse.swt.graphics.GC;
52 import org.eclipse.swt.graphics.Image;
53 import org.eclipse.swt.graphics.ImageData;
54 import org.eclipse.swt.graphics.PaletteData;
55 import org.eclipse.swt.graphics.Point;
56 import org.eclipse.swt.graphics.RGB;
57 import org.eclipse.swt.widgets.Caret;
58 import org.eclipse.swt.widgets.Composite;
59 import org.eclipse.swt.widgets.Control;
60 import org.eclipse.swt.widgets.Display;
61 import org.eclipse.swt.widgets.Menu;
62 import org.eclipse.swt.widgets.Shell;
63
64 import org.eclipse.core.commands.operations.IOperationApprover;
65 import org.eclipse.core.commands.operations.IOperationHistory;
66 import org.eclipse.core.commands.operations.IUndoContext;
67 import org.eclipse.core.commands.operations.OperationHistoryFactory;
68
69 import org.eclipse.core.runtime.Assert;
70 import org.eclipse.core.runtime.CoreException;
71 import org.eclipse.core.runtime.IConfigurationElement;
72 import org.eclipse.core.runtime.ILog;
73 import org.eclipse.core.runtime.IProgressMonitor;
74 import org.eclipse.core.runtime.IStatus;
75 import org.eclipse.core.runtime.NullProgressMonitor;
76 import org.eclipse.core.runtime.Platform;
77 import org.eclipse.core.runtime.SafeRunner;
78 import org.eclipse.core.runtime.Status;
79
80 import org.eclipse.text.undo.DocumentUndoManagerRegistry;
81 import org.eclipse.text.undo.IDocumentUndoManager;
82
83 import org.eclipse.jface.action.Action;
84 import org.eclipse.jface.action.GroupMarker;
85 import org.eclipse.jface.action.IAction;
86 import org.eclipse.jface.action.IMenuListener;
87 import org.eclipse.jface.action.IMenuManager;
88 import org.eclipse.jface.action.IStatusLineManager;
89 import org.eclipse.jface.action.MenuManager;
90 import org.eclipse.jface.action.Separator;
91 import org.eclipse.jface.dialogs.ErrorDialog;
92 import org.eclipse.jface.dialogs.MessageDialog;
93 import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
94 import org.eclipse.jface.operation.IRunnableWithProgress;
95 import org.eclipse.jface.preference.IPreferenceStore;
96 import org.eclipse.jface.preference.PreferenceConverter;
97 import org.eclipse.jface.resource.ImageDescriptor;
98 import org.eclipse.jface.resource.JFaceResources;
99 import org.eclipse.jface.util.IPropertyChangeListener;
100 import org.eclipse.jface.util.PropertyChangeEvent;
101 import org.eclipse.jface.util.SafeRunnable;
102 import org.eclipse.jface.viewers.IPostSelectionProvider;
103 import org.eclipse.jface.viewers.ISelection;
104 import org.eclipse.jface.viewers.ISelectionChangedListener;
105 import org.eclipse.jface.viewers.ISelectionProvider;
106 import org.eclipse.jface.viewers.SelectionChangedEvent;
107 import org.eclipse.jface.viewers.StructuredSelection;
108 import org.eclipse.jface.window.IShellProvider;
109
110 import org.eclipse.jface.text.AbstractInformationControlManager;
111 import org.eclipse.jface.text.BadLocationException;
112 import org.eclipse.jface.text.DefaultInformationControl;
113 import org.eclipse.jface.text.DefaultLineTracker;
114 import org.eclipse.jface.text.DocumentEvent;
115 import org.eclipse.jface.text.IDocument;
116 import org.eclipse.jface.text.IDocumentListener;
117 import org.eclipse.jface.text.IFindReplaceTarget;
118 import org.eclipse.jface.text.IFindReplaceTargetExtension;
119 import org.eclipse.jface.text.IInformationControl;
120 import org.eclipse.jface.text.IInformationControlCreator;
121 import org.eclipse.jface.text.IMarkRegionTarget;
122 import org.eclipse.jface.text.IRegion;
123 import org.eclipse.jface.text.IRewriteTarget;
124 import org.eclipse.jface.text.ISelectionValidator;
125 import org.eclipse.jface.text.ITextHover;
126 import org.eclipse.jface.text.ITextInputListener;
127 import org.eclipse.jface.text.ITextListener;
128 import org.eclipse.jface.text.ITextOperationTarget;
129 import org.eclipse.jface.text.ITextSelection;
130 import org.eclipse.jface.text.ITextViewer;
131 import org.eclipse.jface.text.ITextViewerExtension;
132 import org.eclipse.jface.text.ITextViewerExtension2;
133 import org.eclipse.jface.text.ITextViewerExtension4;
134 import org.eclipse.jface.text.ITextViewerExtension5;
135 import org.eclipse.jface.text.ITextViewerExtension6;
136 import org.eclipse.jface.text.ITextViewerExtension7;
137 import org.eclipse.jface.text.IUndoManager;
138 import org.eclipse.jface.text.IUndoManagerExtension;
139 import org.eclipse.jface.text.Position;
140 import org.eclipse.jface.text.Region;
141 import org.eclipse.jface.text.TabsToSpacesConverter;
142 import org.eclipse.jface.text.TextEvent;
143 import org.eclipse.jface.text.TextSelection;
144 import org.eclipse.jface.text.TextUtilities;
145 import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
146 import org.eclipse.jface.text.information.IInformationProvider;
147 import org.eclipse.jface.text.information.IInformationProviderExtension;
148 import org.eclipse.jface.text.information.IInformationProviderExtension2;
149 import org.eclipse.jface.text.information.InformationPresenter;
150 import org.eclipse.jface.text.link.LinkedModeModel;
151 import org.eclipse.jface.text.link.LinkedPosition;
152 import org.eclipse.jface.text.revisions.RevisionInformation;
153 import org.eclipse.jface.text.source.Annotation;
154 import org.eclipse.jface.text.source.CompositeRuler;
155 import org.eclipse.jface.text.source.IAnnotationHover;
156 import org.eclipse.jface.text.source.IAnnotationHoverExtension;
157 import org.eclipse.jface.text.source.IAnnotationModel;
158 import org.eclipse.jface.text.source.ILineRange;
159 import org.eclipse.jface.text.source.ISourceViewer;
160 import org.eclipse.jface.text.source.ISourceViewerExtension3;
161 import org.eclipse.jface.text.source.IVerticalRuler;
162 import org.eclipse.jface.text.source.IVerticalRulerColumn;
163 import org.eclipse.jface.text.source.IVerticalRulerExtension;
164 import org.eclipse.jface.text.source.IVerticalRulerInfo;
165 import org.eclipse.jface.text.source.SourceViewer;
166 import org.eclipse.jface.text.source.SourceViewerConfiguration;
167 import org.eclipse.jface.text.source.VerticalRuler;
168
169 import org.eclipse.ui.IActionBars;
170 import org.eclipse.ui.IEditorDescriptor;
171 import org.eclipse.ui.IEditorInput;
172 import org.eclipse.ui.IEditorPart;
173 import org.eclipse.ui.IEditorRegistry;
174 import org.eclipse.ui.IEditorSite;
175 import org.eclipse.ui.IKeyBindingService;
176 import org.eclipse.ui.IMemento;
177 import org.eclipse.ui.INavigationLocation;
178 import org.eclipse.ui.INavigationLocationProvider;
179 import org.eclipse.ui.IPartListener;
180 import org.eclipse.ui.IPartService;
181 import org.eclipse.ui.IPersistableEditor;
182 import org.eclipse.ui.IReusableEditor;
183 import org.eclipse.ui.ISaveablesLifecycleListener;
184 import org.eclipse.ui.ISaveablesSource;
185 import org.eclipse.ui.IWindowListener;
186 import org.eclipse.ui.IWorkbenchActionConstants;
187 import org.eclipse.ui.IWorkbenchPart;
188 import org.eclipse.ui.IWorkbenchWindow;
189 import org.eclipse.ui.PartInitException;
190 import org.eclipse.ui.PlatformUI;
191 import org.eclipse.ui.Saveable;
192 import org.eclipse.ui.SaveablesLifecycleEvent;
193 import org.eclipse.ui.actions.CommandNotMappedException;
194 import org.eclipse.ui.actions.ContributedAction;
195 import org.eclipse.ui.dialogs.PropertyDialogAction;
196 import org.eclipse.ui.dnd.IDragAndDropService;
197 import org.eclipse.ui.internal.texteditor.EditPosition;
198 import org.eclipse.ui.internal.texteditor.NLSUtility;
199 import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
200 import org.eclipse.ui.internal.texteditor.rulers.StringSetSerializer;
201 import org.eclipse.ui.operations.LinearUndoViolationUserApprover;
202 import org.eclipse.ui.operations.NonLocalUndoUserApprover;
203 import org.eclipse.ui.operations.OperationHistoryActionHandler;
204 import org.eclipse.ui.operations.RedoActionHandler;
205 import org.eclipse.ui.operations.UndoActionHandler;
206 import org.eclipse.ui.part.EditorPart;
207 import org.eclipse.ui.texteditor.rulers.IColumnSupport;
208 import org.eclipse.ui.texteditor.rulers.IContributedRulerColumn;
209 import org.eclipse.ui.texteditor.rulers.RulerColumnDescriptor;
210 import org.eclipse.ui.texteditor.rulers.RulerColumnPreferenceAdapter;
211 import org.eclipse.ui.texteditor.rulers.RulerColumnRegistry;
212
213
214 /**
215  * Abstract base implementation of a text editor.
216  * <p>
217  * Subclasses are responsible for configuring the editor appropriately.
218  * The standard text editor, <code>TextEditor</code>, is one such example.</p>
219  * <p>
220  * If a subclass calls {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId} the argument is
221  * used as the id under which the editor's context menu is registered for extensions.
222  * If no id is set, the context menu is registered under <b>[editor_id].EditorContext</b>
223  * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to
224  * run in version 1.0 context menu registration compatibility mode, the latter form of the
225  * registration even happens if a context menu id has been set via {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}.
226  * If no id is set while in compatibility mode, the menu is registered under
227  * {@link #DEFAULT_EDITOR_CONTEXT_MENU_ID}.</p>
228  * <p>
229  * If a subclass calls {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} the argument is
230  * used as the id under which the ruler's context menu is registered for extensions.
231  * If no id is set, the context menu is registered under <b>[editor_id].RulerContext</b>
232  * whereby [editor_id] is replaced with the editor's part id. If the editor is instructed to
233  * run in version 1.0 context menu registration compatibility mode, the latter form of the
234  * registration even happens if a context menu id has been set via {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId}.
235  * If no id is set while in compatibility mode, the menu is registered under
236  * {@link #DEFAULT_RULER_CONTEXT_MENU_ID}.</p>
237  */

238 public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, INavigationLocationProvider, ISaveablesSource, IPersistableEditor {
239
240     /**
241      * Tag used in xml configuration files to specify editor action contributions.
242      * Current value: <code>editorContribution</code>
243      * @since 2.0
244      */

245     private static final String JavaDoc TAG_CONTRIBUTION_TYPE= "editorContribution"; //$NON-NLS-1$
246

247     /**
248      * Tags used in the {@link IMemento} when saving and
249      * restoring editor state.
250      *
251      * @see #saveState(IMemento)
252      * @see #restoreState(IMemento)
253      * @since 3.3
254      */

255     protected static final String JavaDoc TAG_SELECTION_OFFSET= "selectionOffset"; //$NON-NLS-1$
256
protected static final String JavaDoc TAG_SELECTION_LENGTH= "selectionLength"; //$NON-NLS-1$
257
// XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=168524
258
private static final String JavaDoc TAG_SELECTION_HPIXEL= "selectionHPixel"; //$NON-NLS-1$
259

260
261     /**
262      * The caret width for the wide (double) caret.
263      * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
264      * Value: {@value}
265      * @since 3.0
266      */

267     private static final int WIDE_CARET_WIDTH= 2;
268
269     /**
270      * The caret width for the narrow (single) caret.
271      * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
272      * Value: {@value}
273      * @since 3.0
274      */

275     private static final int SINGLE_CARET_WIDTH= 1;
276
277     /**
278      * The text input listener.
279      *
280      * @see ITextInputListener
281      * @since 2.1
282      */

283     private static class TextInputListener implements ITextInputListener {
284         /** Indicates whether the editor input changed during the process of state validation. */
285         public boolean inputChanged;
286
287         /* Detectors for editor input changes during the process of state validation. */
288         public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {}
289         public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { inputChanged= true; }
290     }
291
292     /**
293      * Internal element state listener.
294      */

295     class ElementStateListener implements IElementStateListener, IElementStateListenerExtension {
296
297             /**
298              * Internal <code>VerifyListener</code> for performing the state validation of the
299              * editor input in case of the first attempted manipulation via typing on the keyboard.
300              * @since 2.0
301              */

302             class Validator implements VerifyListener {
303                 /*
304                  * @see VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
305                  */

306                 public void verifyText(VerifyEvent e) {
307                     IDocument document= getDocumentProvider().getDocument(getEditorInput());
308                     final boolean[] documentChanged= new boolean[1];
309                     IDocumentListener listener= new IDocumentListener() {
310                         public void documentAboutToBeChanged(DocumentEvent event) {
311                         }
312                         public void documentChanged(DocumentEvent event) {
313                             documentChanged[0]= true;
314                         }
315                     };
316                     try {
317                         if (document != null)
318                             document.addDocumentListener(listener);
319                         if (! validateEditorInputState() || documentChanged[0])
320                             e.doit= false;
321                     } finally {
322                         if (document != null)
323                             document.removeDocumentListener(listener);
324                     }
325                 }
326             }
327
328         /**
329          * The listener's validator.
330          * @since 2.0
331          */

332         private Validator fValidator;
333         /**
334          * The display used for posting runnable into the UI thread.
335          * @since 3.0
336          */

337         private Display fDisplay;
338
339         /*
340          * @see IElementStateListenerExtension#elementStateValidationChanged(Object, boolean)
341          * @since 2.0
342          */

343         public void elementStateValidationChanged(final Object JavaDoc element, final boolean isStateValidated) {
344             if (element != null && element.equals(getEditorInput())) {
345                 Runnable JavaDoc r= new Runnable JavaDoc() {
346                     public void run() {
347                         enableSanityChecking(true);
348                         if (isStateValidated && fValidator != null) {
349                             ISourceViewer viewer= fSourceViewer;
350                             if (viewer != null) {
351                                 StyledText textWidget= viewer.getTextWidget();
352                                 if (textWidget != null && !textWidget.isDisposed())
353                                     textWidget.removeVerifyListener(fValidator);
354                                 fValidator= null;
355                                 enableStateValidation(false);
356                             }
357                         } else if (!isStateValidated && fValidator == null) {
358                             ISourceViewer viewer= fSourceViewer;
359                             if (viewer != null) {
360                                 StyledText textWidget= viewer.getTextWidget();
361                                 if (textWidget != null && !textWidget.isDisposed()) {
362                                     fValidator= new Validator();
363                                     enableStateValidation(true);
364                                     textWidget.addVerifyListener(fValidator);
365                                 }
366                             }
367                         }
368                     }
369                 };
370                 execute(r, false);
371             }
372         }
373
374
375         /*
376          * @see IElementStateListener#elementDirtyStateChanged(Object, boolean)
377          */

378         public void elementDirtyStateChanged(Object JavaDoc element, boolean isDirty) {
379             if (element != null && element.equals(getEditorInput())) {
380                 Runnable JavaDoc r= new Runnable JavaDoc() {
381                     public void run() {
382                         enableSanityChecking(true);
383                         firePropertyChange(PROP_DIRTY);
384                     }
385                 };
386                 execute(r, false);
387             }
388         }
389
390         /*
391          * @see IElementStateListener#elementContentAboutToBeReplaced(Object)
392          */

393         public void elementContentAboutToBeReplaced(Object JavaDoc element) {
394             if (element != null && element.equals(getEditorInput())) {
395                 Runnable JavaDoc r= new Runnable JavaDoc() {
396                     public void run() {
397                         enableSanityChecking(true);
398                         rememberSelection();
399                         resetHighlightRange();
400                     }
401                 };
402                 execute(r, false);
403             }
404         }
405
406         /*
407          * @see IElementStateListener#elementContentReplaced(Object)
408          */

409         public void elementContentReplaced(Object JavaDoc element) {
410             if (element != null && element.equals(getEditorInput())) {
411                 Runnable JavaDoc r= new Runnable JavaDoc() {
412                     public void run() {
413                         enableSanityChecking(true);
414                         firePropertyChange(PROP_DIRTY);
415                         restoreSelection();
416                         handleElementContentReplaced();
417                     }
418                 };
419                 execute(r, false);
420             }
421         }
422
423         /*
424          * @see IElementStateListener#elementDeleted(Object)
425          */

426         public void elementDeleted(Object JavaDoc deletedElement) {
427             if (deletedElement != null && deletedElement.equals(getEditorInput())) {
428                 Runnable JavaDoc r= new Runnable JavaDoc() {
429                     public void run() {
430                         enableSanityChecking(true);
431                         close(false);
432                     }
433                 };
434                 execute(r, false);
435             }
436         }
437
438         /*
439          * @see IElementStateListener#elementMoved(Object, Object)
440          */

441         public void elementMoved(final Object JavaDoc originalElement, final Object JavaDoc movedElement) {
442             if (originalElement != null && originalElement.equals(getEditorInput())) {
443                 final boolean doValidationAsync= Display.getCurrent() != null;
444                 Runnable JavaDoc r= new Runnable JavaDoc() {
445                     public void run() {
446                         enableSanityChecking(true);
447
448                         if (fSourceViewer == null)
449                             return;
450
451                         if (!canHandleMove((IEditorInput) originalElement, (IEditorInput) movedElement)) {
452                             close(true);
453                             return;
454                         }
455
456                         if (movedElement == null || movedElement instanceof IEditorInput) {
457                             rememberSelection();
458
459                             final IDocumentProvider d= getDocumentProvider();
460                             final String JavaDoc previousContent;
461                             IDocumentUndoManager previousUndoManager=null;
462                             IDocument changed= null;
463                             boolean wasDirty= isDirty();
464                             changed= d.getDocument(getEditorInput());
465                             if (changed != null) {
466                                 if (wasDirty)
467                                     previousContent= changed.get();
468                                 else
469                                     previousContent= null;
470                                 
471                                 previousUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(changed);
472                                 if (previousUndoManager != null)
473                                     previousUndoManager.connect(this);
474                             }
475                             else
476                                 previousContent= null;
477                             
478                             setInput((IEditorInput) movedElement);
479                             
480                             // The undo manager needs to be replaced with one for the new document.
481
// Transfer the undo history and then disconnect from the old undo manager.
482
if (previousUndoManager != null) {
483                                 IDocument newDocument= getDocumentProvider().getDocument(movedElement);
484                                 if (newDocument != null) {
485                                     IDocumentUndoManager newUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(newDocument);
486                                     if (newUndoManager != null)
487                                         newUndoManager.transferUndoHistory(previousUndoManager);
488                                 }
489                                 previousUndoManager.disconnect(this);
490                             }
491
492                             if (wasDirty && changed != null) {
493                                 Runnable JavaDoc r2= new Runnable JavaDoc() {
494                                     public void run() {
495                                         validateState(getEditorInput());
496                                         d.getDocument(getEditorInput()).set(previousContent);
497                                         updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
498                                         restoreSelection();
499                                     }
500                                 };
501                                 execute(r2, doValidationAsync);
502                             } else
503                                 restoreSelection();
504
505                         }
506                     }
507                 };
508                 execute(r, false);
509             }
510         }
511
512         /*
513          * @see IElementStateListenerExtension#elementStateChanging(Object)
514          * @since 2.0
515          */

516         public void elementStateChanging(Object JavaDoc element) {
517             if (element != null && element.equals(getEditorInput()))
518                 enableSanityChecking(false);
519         }
520
521         /*
522          * @see IElementStateListenerExtension#elementStateChangeFailed(Object)
523          * @since 2.0
524          */

525         public void elementStateChangeFailed(Object JavaDoc element) {
526             if (element != null && element.equals(getEditorInput()))
527                 enableSanityChecking(true);
528         }
529
530         /**
531          * Executes the given runnable in the UI thread.
532          * <p>
533          * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76765 for details
534          * about why the parameter <code>postAsync</code> has been
535          * introduced in the course of 3.1.
536          *
537          * @param runnable runnable to be executed
538          * @param postAsync <code>true</code> if the runnable must be posted asynchronous, <code>false</code> otherwise
539          * @since 3.0
540          */

541         private void execute(Runnable JavaDoc runnable, boolean postAsync) {
542             if (postAsync || Display.getCurrent() == null) {
543                 if (fDisplay == null)
544                     fDisplay= getSite().getShell().getDisplay();
545                 fDisplay.asyncExec(runnable);
546             } else
547                 runnable.run();
548         }
549     }
550
551     /**
552      * Internal text listener for updating all content dependent
553      * actions. The updating is done asynchronously.
554      */

555     class TextListener implements ITextListener, ITextInputListener {
556
557         /** The posted updater code. */
558         private Runnable JavaDoc fRunnable= new Runnable JavaDoc() {
559             public void run() {
560                 fIsRunnablePosted= false;
561
562                 if (fSourceViewer != null) {
563                     updateContentDependentActions();
564
565                     // remember the last edit position
566
if (isDirty() && fUpdateLastEditPosition) {
567                         fUpdateLastEditPosition= false;
568                         ISelection sel= getSelectionProvider().getSelection();
569                         IEditorInput input= getEditorInput();
570                         IDocument document= getDocumentProvider().getDocument(input);
571
572                         if (fLocalLastEditPosition != null) {
573                             document.removePosition(fLocalLastEditPosition);
574                             fLocalLastEditPosition= null;
575                         }
576
577                         if (sel instanceof ITextSelection && !sel.isEmpty()) {
578                             ITextSelection s= (ITextSelection) sel;
579                             fLocalLastEditPosition= new Position(s.getOffset(), s.getLength());
580                             try {
581                                 document.addPosition(fLocalLastEditPosition);
582                             } catch (BadLocationException ex) {
583                                 fLocalLastEditPosition= null;
584                             }
585                         }
586                         TextEditorPlugin.getDefault().setLastEditPosition(new EditPosition(input, getEditorSite().getId(), fLocalLastEditPosition));
587                     }
588                 }
589             }
590         };
591
592         /** Display used for posting the updater code. */
593         private Display fDisplay;
594         /**
595          * The editor's last edit position
596          * @since 3.0
597          */

598         private Position fLocalLastEditPosition;
599         /**
600          * Has the runnable been posted?
601          * @since 3.0
602          */

603         private boolean fIsRunnablePosted= false;
604         /**
605          * Should the last edit position be updated?
606          * @since 3.0
607          */

608         private boolean fUpdateLastEditPosition= false;
609
610         /*
611          * @see ITextListener#textChanged(TextEvent)
612          */

613         public void textChanged(TextEvent event) {
614
615             /*
616              * Also works for text events which do not base on a DocumentEvent.
617              * This way, if the visible document of the viewer changes, all content
618              * dependent actions are updated as well.
619              */

620
621             if (fDisplay == null)
622                 fDisplay= getSite().getShell().getDisplay();
623
624             if (event.getDocumentEvent() != null)
625                 fUpdateLastEditPosition= true;
626
627             if (!fIsRunnablePosted) {
628                 fIsRunnablePosted= true;
629                 fDisplay.asyncExec(fRunnable);
630             }
631         }
632
633         /*
634          * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
635          */

636         public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
637             if (oldInput != null && fLocalLastEditPosition != null) {
638                 oldInput.removePosition(fLocalLastEditPosition);
639                 fLocalLastEditPosition= null;
640             }
641         }
642
643         /*
644          * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
645          */

646         public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
647         }
648     }
649
650     /**
651      * Internal property change listener for handling changes in the editor's preferences.
652      */

653     class PropertyChangeListener implements IPropertyChangeListener {
654         /*
655          * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
656          */

657         public void propertyChange(PropertyChangeEvent event) {
658             handlePreferenceStoreChanged(event);
659         }
660     }
661
662     /**
663      * Internal property change listener for handling workbench font changes.
664      * @since 2.1
665      */

666     class FontPropertyChangeListener implements IPropertyChangeListener {
667         /*