1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
14  *     Michel Ishizuka (cqw10305@nifty.com) - http://bugs.eclipse.org/bugs/show_bug.cgi?id=68963
15  *     Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668
16  *     Benjamin Muskalla <b.muskalla@gmx.net> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573
17  *     Stephan Wahlbrink <stephan.wahlbrink@walware.de> - Wrong operations mode/feedback for text drag over/drop in text editors - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206043
18  *     Tom Eicher (Avaloq Evolution AG) - block selection mode
19  *     Nick Sandonato <nsandona@us.ibm.com> - [implementation] AbstractTextEditor does not prompt when out of sync in MultiPageEditorPart - http://bugs.eclipse.org/337719
20  *     Holger Voormann - Word Wrap - https://bugs.eclipse.org/bugs/show_bug.cgi?id=35779
21  *     Florian Weßling <flo@cdhq.de> - Word Wrap - https://bugs.eclipse.org/bugs/show_bug.cgi?id=35779
22  *     Andrey Loskutov <loskutov@gmx.de> - Word Wrap - https://bugs.eclipse.org/bugs/show_bug.cgi?id=35779
23  *     Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419
24  *******************************************************************************/
25 package org.eclipse.ui.texteditor;
26 
27 import java.lang.reflect.InvocationTargetException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.ResourceBundle;
35 import java.util.concurrent.atomic.AtomicInteger;
36 
37 import org.osgi.framework.Bundle;
38 
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.custom.BusyIndicator;
41 import org.eclipse.swt.custom.ST;
42 import org.eclipse.swt.custom.StyledText;
43 import org.eclipse.swt.custom.VerifyKeyListener;
44 import org.eclipse.swt.dnd.DND;
45 import org.eclipse.swt.dnd.DragSource;
46 import org.eclipse.swt.dnd.DragSourceAdapter;
47 import org.eclipse.swt.dnd.DragSourceEvent;
48 import org.eclipse.swt.dnd.DropTargetAdapter;
49 import org.eclipse.swt.dnd.DropTargetEvent;
50 import org.eclipse.swt.dnd.DropTargetListener;
51 import org.eclipse.swt.dnd.TextTransfer;
52 import org.eclipse.swt.dnd.Transfer;
53 import org.eclipse.swt.events.GestureEvent;
54 import org.eclipse.swt.events.GestureListener;
55 import org.eclipse.swt.events.KeyEvent;
56 import org.eclipse.swt.events.KeyListener;
57 import org.eclipse.swt.events.MouseEvent;
58 import org.eclipse.swt.events.MouseListener;
59 import org.eclipse.swt.events.VerifyEvent;
60 import org.eclipse.swt.events.VerifyListener;
61 import org.eclipse.swt.graphics.Color;
62 import org.eclipse.swt.graphics.Font;
63 import org.eclipse.swt.graphics.FontData;
64 import org.eclipse.swt.graphics.GC;
65 import org.eclipse.swt.graphics.Image;
66 import org.eclipse.swt.graphics.ImageData;
67 import org.eclipse.swt.graphics.PaletteData;
68 import org.eclipse.swt.graphics.Point;
69 import org.eclipse.swt.graphics.RGB;
70 import org.eclipse.swt.widgets.Caret;
71 import org.eclipse.swt.widgets.Composite;
72 import org.eclipse.swt.widgets.Control;
73 import org.eclipse.swt.widgets.Display;
74 import org.eclipse.swt.widgets.Event;
75 import org.eclipse.swt.widgets.Menu;
76 import org.eclipse.swt.widgets.Shell;
77 
78 import org.eclipse.core.commands.operations.IOperationApprover;
79 import org.eclipse.core.commands.operations.IOperationHistory;
80 import org.eclipse.core.commands.operations.IUndoContext;
81 import org.eclipse.core.commands.operations.OperationHistoryFactory;
82 
83 import org.eclipse.core.runtime.Assert;
84 import org.eclipse.core.runtime.CoreException;
85 import org.eclipse.core.runtime.IConfigurationElement;
86 import org.eclipse.core.runtime.ILog;
87 import org.eclipse.core.runtime.IProgressMonitor;
88 import org.eclipse.core.runtime.IStatus;
89 import org.eclipse.core.runtime.NullProgressMonitor;
90 import org.eclipse.core.runtime.Platform;
91 import org.eclipse.core.runtime.SafeRunner;
92 import org.eclipse.core.runtime.Status;
93 
94 import org.eclipse.text.undo.DocumentUndoManagerRegistry;
95 import org.eclipse.text.undo.IDocumentUndoManager;
96 
97 import org.eclipse.jface.action.Action;
98 import org.eclipse.jface.action.GroupMarker;
99 import org.eclipse.jface.action.IAction;
100 import org.eclipse.jface.action.IMenuListener;
101 import org.eclipse.jface.action.IMenuManager;
102 import org.eclipse.jface.action.IStatusLineManager;
103 import org.eclipse.jface.action.MenuManager;
104 import org.eclipse.jface.action.Separator;
105 import org.eclipse.jface.dialogs.ErrorDialog;
106 import org.eclipse.jface.dialogs.MessageDialog;
107 import org.eclipse.jface.operation.IRunnableWithProgress;
108 import org.eclipse.jface.preference.IPreferenceStore;
109 import org.eclipse.jface.preference.PreferenceConverter;
110 import org.eclipse.jface.resource.ImageDescriptor;
111 import org.eclipse.jface.resource.JFaceResources;
112 import org.eclipse.jface.util.IPropertyChangeListener;
113 import org.eclipse.jface.util.PropertyChangeEvent;
114 import org.eclipse.jface.util.SafeRunnable;
115 import org.eclipse.jface.viewers.IPostSelectionProvider;
116 import org.eclipse.jface.viewers.ISelection;
117 import org.eclipse.jface.viewers.ISelectionChangedListener;
118 import org.eclipse.jface.viewers.ISelectionProvider;
119 import org.eclipse.jface.viewers.SelectionChangedEvent;
120 import org.eclipse.jface.viewers.StructuredSelection;
121 
122 import org.eclipse.jface.text.AbstractInformationControlManager;
123 import org.eclipse.jface.text.BadLocationException;
124 import org.eclipse.jface.text.BlockTextSelection;
125 import org.eclipse.jface.text.DefaultLineTracker;
126 import org.eclipse.jface.text.DocumentEvent;
127 import org.eclipse.jface.text.IDocument;
128 import org.eclipse.jface.text.IDocumentListener;
129 import org.eclipse.jface.text.IFindReplaceTarget;
130 import org.eclipse.jface.text.IFindReplaceTargetExtension;
131 import org.eclipse.jface.text.IInformationControlCreator;
132 import org.eclipse.jface.text.IMarkRegionTarget;
133 import org.eclipse.jface.text.IRegion;
134 import org.eclipse.jface.text.IRewriteTarget;
135 import org.eclipse.jface.text.ISelectionValidator;
136 import org.eclipse.jface.text.ITextHover;
137 import org.eclipse.jface.text.ITextInputListener;
138 import org.eclipse.jface.text.ITextListener;
139 import org.eclipse.jface.text.ITextOperationTarget;
140 import org.eclipse.jface.text.ITextSelection;
141 import org.eclipse.jface.text.ITextViewer;
142 import org.eclipse.jface.text.ITextViewerExtension;
143 import org.eclipse.jface.text.ITextViewerExtension2;
144 import org.eclipse.jface.text.ITextViewerExtension4;
145 import org.eclipse.jface.text.ITextViewerExtension5;
146 import org.eclipse.jface.text.ITextViewerExtension6;
147 import org.eclipse.jface.text.ITextViewerExtension7;
148 import org.eclipse.jface.text.ITextViewerExtension8;
149 import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode;
150 import org.eclipse.jface.text.IUndoManager;
151 import org.eclipse.jface.text.IUndoManagerExtension;
152 import org.eclipse.jface.text.Position;
153 import org.eclipse.jface.text.Region;
154 import org.eclipse.jface.text.TabsToSpacesConverter;
155 import org.eclipse.jface.text.TextEvent;
156 import org.eclipse.jface.text.TextSelection;
157 import org.eclipse.jface.text.TextUtilities;
158 import org.eclipse.jface.text.codemining.ICodeMiningProvider;
159 import org.eclipse.jface.text.hyperlink.HyperlinkManager;
160 import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
161 import org.eclipse.jface.text.information.IInformationProvider;
162 import org.eclipse.jface.text.information.IInformationProviderExtension2;
163 import org.eclipse.jface.text.link.LinkedModeModel;
164 import org.eclipse.jface.text.link.LinkedPosition;
165 import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
166 import org.eclipse.jface.text.revisions.RevisionInformation;
167 import org.eclipse.jface.text.source.Annotation;
168 import org.eclipse.jface.text.source.CompositeRuler;
169 import org.eclipse.jface.text.source.IAnnotationHover;
170 import org.eclipse.jface.text.source.IAnnotationModel;
171 import org.eclipse.jface.text.source.ISourceViewer;
172 import org.eclipse.jface.text.source.ISourceViewerExtension3;
173 import org.eclipse.jface.text.source.ISourceViewerExtension4;
174 import org.eclipse.jface.text.source.ISourceViewerExtension5;
175 import org.eclipse.jface.text.source.IVerticalRuler;
176 import org.eclipse.jface.text.source.IVerticalRulerColumn;
177 import org.eclipse.jface.text.source.IVerticalRulerExtension;
178 import org.eclipse.jface.text.source.IVerticalRulerInfo;
179 import org.eclipse.jface.text.source.SourceViewer;
180 import org.eclipse.jface.text.source.SourceViewerConfiguration;
181 import org.eclipse.jface.text.source.VerticalRuler;
182 
183 import org.eclipse.ui.IActionBars;
184 import org.eclipse.ui.IEditorDescriptor;
185 import org.eclipse.ui.IEditorInput;
186 import org.eclipse.ui.IEditorPart;
187 import org.eclipse.ui.IEditorRegistry;
188 import org.eclipse.ui.IEditorSite;
189 import org.eclipse.ui.IKeyBindingService;
190 import org.eclipse.ui.IMemento;
191 import org.eclipse.ui.INavigationLocation;
192 import org.eclipse.ui.INavigationLocationProvider;
193 import org.eclipse.ui.IPartListener;
194 import org.eclipse.ui.IPartService;
195 import org.eclipse.ui.IPersistableEditor;
196 import org.eclipse.ui.IReusableEditor;
197 import org.eclipse.ui.ISaveablesLifecycleListener;
198 import org.eclipse.ui.ISaveablesSource;
199 import org.eclipse.ui.IWindowListener;
200 import org.eclipse.ui.IWorkbenchActionConstants;
201 import org.eclipse.ui.IWorkbenchCommandConstants;
202 import org.eclipse.ui.IWorkbenchPart;
203 import org.eclipse.ui.IWorkbenchPartSite;
204 import org.eclipse.ui.IWorkbenchWindow;
205 import org.eclipse.ui.PartInitException;
206 import org.eclipse.ui.PlatformUI;
207 import org.eclipse.ui.Saveable;
208 import org.eclipse.ui.SaveablesLifecycleEvent;
209 import org.eclipse.ui.actions.ActionFactory;
210 import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
211 import org.eclipse.ui.actions.CommandNotMappedException;
212 import org.eclipse.ui.actions.ContributedAction;
213 import org.eclipse.ui.commands.ICommandImageService;
214 import org.eclipse.ui.dialogs.PropertyDialogAction;
215 import org.eclipse.ui.dnd.IDragAndDropService;
216 import org.eclipse.ui.internal.texteditor.EditPosition;
217 import org.eclipse.ui.internal.texteditor.FocusedInformationPresenter;
218 import org.eclipse.ui.internal.texteditor.NLSUtility;
219 import org.eclipse.ui.internal.texteditor.TextEditorPlugin;
220 import org.eclipse.ui.internal.texteditor.rulers.StringSetSerializer;
221 import org.eclipse.ui.operations.LinearUndoViolationUserApprover;
222 import org.eclipse.ui.operations.NonLocalUndoUserApprover;
223 import org.eclipse.ui.operations.OperationHistoryActionHandler;
224 import org.eclipse.ui.operations.RedoActionHandler;
225 import org.eclipse.ui.operations.UndoActionHandler;
226 import org.eclipse.ui.part.EditorPart;
227 import org.eclipse.ui.part.MultiPageEditorSite;
228 
229 import org.eclipse.ui.texteditor.rulers.IColumnSupport;
230 import org.eclipse.ui.texteditor.rulers.IContributedRulerColumn;
231 import org.eclipse.ui.texteditor.rulers.RulerColumnDescriptor;
232 import org.eclipse.ui.texteditor.rulers.RulerColumnPreferenceAdapter;
233 import org.eclipse.ui.texteditor.rulers.RulerColumnRegistry;
234 
235 
236 /**
237  * Abstract base implementation of a text editor.
238  * <p>
239  * Subclasses are responsible for configuring the editor appropriately. The standard text editor,
240  * <code>TextEditor</code>, is one such example.
241  * </p>
242  * <p>
243  * If a subclass calls {@linkplain #setEditorContextMenuId(String) setEditorContextMenuId} the
244  * argument is used as the id under which the editor's context menu is registered for extensions. If
245  * no id is set, the context menu is registered under <b>[editor_id].EditorContext</b> whereby
246  * [editor_id] is replaced with the editor's part id. If the editor is instructed to run in version
247  * 1.0 context menu registration compatibility mode, the latter form of the registration even
248  * happens if a context menu id has been set via {@linkplain #setEditorContextMenuId(String)
249  * setEditorContextMenuId}. If no id is set while in compatibility mode, the menu is registered
250  * under {@link #DEFAULT_EDITOR_CONTEXT_MENU_ID}.
251  * </p>
252  * <p>
253  * If a subclass calls {@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} the
254  * argument is used as the id under which the ruler's context menu is registered for extensions. If
255  * no id is set, the context menu is registered under <b>[editor_id].RulerContext</b> whereby
256  * [editor_id] is replaced with the editor's part id. If the editor is instructed to run in version
257  * 1.0 context menu registration compatibility mode, the latter form of the registration even
258  * happens if a context menu id has been set via {@linkplain #setRulerContextMenuId(String)
259  * setRulerContextMenuId}. If no id is set while in compatibility mode, the menu is registered under
260  * {@link #DEFAULT_RULER_CONTEXT_MENU_ID}.
261  * </p>
262  * <p>
263  * As of 3.5, contributers can contribute editor and ruler context menu actions to all subclasses of
264  * this class by using {@link #COMMON_EDITOR_CONTEXT_MENU_ID} and
265  * {@link #COMMON_RULER_CONTEXT_MENU_ID}.
266  * </p>
267  */
268 public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, ITextEditorExtension5, ITextEditorExtension6, INavigationLocationProvider, ISaveablesSource, IPersistableEditor {
269 
270 	/**
271 	 * Tag used in xml configuration files to specify editor action contributions.
272 	 * Current value: <code>editorContribution</code>
273 	 * @since 2.0
274 	 */
275 	private static final String TAG_CONTRIBUTION_TYPE= "editorContribution"; //$NON-NLS-1$
276 
277 	/**
278 	 * Tag used in the {@link IMemento} when saving and restoring the editor's selection offset.
279 	 *
280 	 * @see #saveState(IMemento)
281 	 * @see #restoreState(IMemento)
282 	 * @see #doRestoreState(IMemento)
283 	 * @since 3.3
284 	 */
285 	protected static final String TAG_SELECTION_OFFSET= "selectionOffset"; //$NON-NLS-1$
286 
287 	/**
288 	 * Tag used in the {@link IMemento} when saving and restoring the editor's selection length.
289 	 *
290 	 * @see #saveState(IMemento)
291 	 * @see #restoreState(IMemento)
292 	 * @see #doRestoreState(IMemento)
293 	 * @since 3.3
294 	 */
295 	protected static final String TAG_SELECTION_LENGTH= "selectionLength"; //$NON-NLS-1$
296 
297 	/**
298 	 * Tag used in the {@link IMemento} when saving and restoring the editor's top pixel value.
299 	 *
300 	 * @see #saveState(IMemento)
301 	 * @see #restoreState(IMemento)
302 	 * @see #doRestoreState(IMemento)
303 	 * @since 3.6
304 	 */
305 	protected static final String TAG_SELECTION_TOP_PIXEL= "selectionTopPixel"; //$NON-NLS-1$
306 
307 	/**
308 	 * Tag used in the {@link IMemento} when saving and restoring the editor's horizontal pixel
309 	 * value.
310 	 *
311 	 * @see #saveState(IMemento)
312 	 * @see #restoreState(IMemento)
313 	 * @see #doRestoreState(IMemento)
314 	 * @since 3.6
315 	 */
316 	protected static final String TAG_SELECTION_HORIZONTAL_PIXEL= "selectionHorizontalPixel"; //$NON-NLS-1$
317 
318 
319 	/**
320 	 * The caret width for the wide (double) caret.
321 	 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
322 	 * Value: {@value}
323 	 * @since 3.0
324 	 */
325 	private static final int WIDE_CARET_WIDTH= 2;
326 
327 	/**
328 	 * The caret width for the narrow (single) caret.
329 	 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=21715.
330 	 * Value: {@value}
331 	 * @since 3.0
332 	 */
333 	private static final int SINGLE_CARET_WIDTH= 1;
334 
335 	/**
336 	 * The symbolic name of the block selection mode font.
337 	 *
338 	 * @since 3.5
339 	 */
340 	private static final String BLOCK_SELECTION_MODE_FONT= "org.eclipse.ui.workbench.texteditor.blockSelectionModeFont"; //$NON-NLS-1$
341 
342 	/**
343 	 * The text input listener.
344 	 *
345 	 * @see ITextInputListener
346 	 * @since 2.1
347 	 */
348 	private static class TextInputListener implements ITextInputListener {
349 		/** Indicates whether the editor input changed during the process of state validation. */
350 		public boolean inputChanged;
351 
352 		/* Detectors for editor input changes during the process of state validation. */
353 		@Override
inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput)354 		public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {}
355 		@Override
inputDocumentChanged(IDocument oldInput, IDocument newInput)356 		public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { inputChanged= true; }
357 	}
358 
359 	/**
360 	 * Internal element state listener.
361 	 */
362 	class ElementStateListener implements IElementStateListener, IElementStateListenerExtension {
363 
364 			/**
365 			 * Internal <code>VerifyListener</code> for performing the state validation of the
366 			 * editor input in case of the first attempted manipulation via typing on the keyboard.
367 			 * @since 2.0
368 			 */
369 			class Validator implements VerifyListener {
370 				@Override
verifyText(VerifyEvent e)371 				public void verifyText(VerifyEvent e) {
372 					IDocument document= getDocumentProvider().getDocument(getEditorInput());
373 					final boolean[] documentChanged= new boolean[1];
374 					IDocumentListener listener= new IDocumentListener() {
375 						@Override
376 						public void documentAboutToBeChanged(DocumentEvent event) {
377 						}
378 						@Override
379 						public void documentChanged(DocumentEvent event) {
380 							documentChanged[0]= true;
381 						}
382 					};
383 					try {
384 						if (document != null)
385 							document.addDocumentListener(listener);
386 						if (! validateEditorInputState() || documentChanged[0])
387 							e.doit= false;
388 					} finally {
389 						if (document != null)
390 							document.removeDocumentListener(listener);
391 					}
392 				}
393 			}
394 
395 		/**
396 		 * The listener's validator.
397 		 * @since 2.0
398 		 */
399 		private Validator fValidator;
400 		/**
401 		 * The display used for posting runnable into the UI thread.
402 		 * @since 3.0
403 		 */
404 		private Display fDisplay;
405 
406 		@Override
elementStateValidationChanged(final Object element, final boolean isStateValidated)407 		public void elementStateValidationChanged(final Object element, final boolean isStateValidated) {
408 			if (element != null && element.equals(getEditorInput())) {
409 				Runnable r = () -> {
410 					enableSanityChecking(true);
411 					if (isStateValidated) {
412 						if (fValidator != null) {
413 							ISourceViewer viewer1 = fSourceViewer;
414 							if (viewer1 != null) {
415 								StyledText textWidget1 = viewer1.getTextWidget();
416 								if (textWidget1 != null && !textWidget1.isDisposed())
417 									textWidget1.removeVerifyListener(fValidator);
418 								fValidator = null;
419 							}
420 						}
421 						enableStateValidation(false);
422 					} else if (!isStateValidated && fValidator == null) {
423 						ISourceViewer viewer2 = fSourceViewer;
424 						if (viewer2 != null) {
425 							StyledText textWidget2 = viewer2.getTextWidget();
426 							if (textWidget2 != null && !textWidget2.isDisposed()) {
427 								fValidator = new Validator();
428 								enableStateValidation(true);
429 								textWidget2.addVerifyListener(fValidator);
430 							}
431 						}
432 					}
433 				};
434 				execute(r, false);
435 			}
436 		}
437 
438 
439 		@Override
elementDirtyStateChanged(Object element, boolean isDirty)440 		public void elementDirtyStateChanged(Object element, boolean isDirty) {
441 			if (element != null && element.equals(getEditorInput())) {
442 				Runnable r = () -> {
443 					enableSanityChecking(true);
444 					firePropertyChange(PROP_DIRTY);
445 				};
446 				execute(r, false);
447 			}
448 		}
449 
450 		@Override
elementContentAboutToBeReplaced(Object element)451 		public void elementContentAboutToBeReplaced(Object element) {
452 			if (element != null && element.equals(getEditorInput())) {
453 				Runnable r = () -> {
454 					enableSanityChecking(true);
455 					rememberSelection();
456 					resetHighlightRange();
457 				};
458 				execute(r, false);
459 			}
460 		}
461 
462 		@Override
elementContentReplaced(Object element)463 		public void elementContentReplaced(Object element) {
464 			if (element != null && element.equals(getEditorInput())) {
465 				Runnable r = () -> {
466 					enableSanityChecking(true);
467 					firePropertyChange(PROP_DIRTY);
468 					restoreSelection();
469 					handleElementContentReplaced();
470 				};
471 				execute(r, false);
472 			}
473 		}
474 
475 		@Override
elementDeleted(Object deletedElement)476 		public void elementDeleted(Object deletedElement) {
477 			if (deletedElement != null && deletedElement.equals(getEditorInput())) {
478 				Runnable r = () -> {
479 					enableSanityChecking(true);
480 					close(false);
481 				};
482 				execute(r, false);
483 			}
484 		}
485 
486 		@Override
elementMoved(final Object originalElement, final Object movedElement)487 		public void elementMoved(final Object originalElement, final Object movedElement) {
488 			if (originalElement != null && originalElement.equals(getEditorInput())) {
489 				final boolean doValidationAsync= Display.getCurrent() != null;
490 				Runnable r= new Runnable() {
491 					@Override
492 					public void run() {
493 						enableSanityChecking(true);
494 
495 						if (fSourceViewer == null)
496 							return;
497 
498 						if (!canHandleMove((IEditorInput) originalElement, (IEditorInput) movedElement)) {
499 							close(true);
500 							return;
501 						}
502 
503 						if (movedElement == null || movedElement instanceof IEditorInput) {
504 							rememberSelection();
505 
506 							final IDocumentProvider d= getDocumentProvider();
507 							final String previousContent;
508 							IDocumentUndoManager previousUndoManager=null;
509 							IDocument changed= null;
510 							boolean wasDirty= isDirty();
511 							changed= d.getDocument(getEditorInput());
512 							if (changed != null) {
513 								if (wasDirty)
514 									previousContent= changed.get();
515 								else
516 									previousContent= null;
517 
518 								previousUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(changed);
519 								if (previousUndoManager != null)
520 									previousUndoManager.connect(this);
521 							}
522 							else
523 								previousContent= null;
524 
525 							setInput((IEditorInput) movedElement);
526 
527 							// The undo manager needs to be replaced with one for the new document.
528 							// Transfer the undo history and then disconnect from the old undo manager.
529 							if (previousUndoManager != null) {
530 								IDocument newDocument= getDocumentProvider().getDocument(movedElement);
531 								if (newDocument != null) {
532 									IDocumentUndoManager newUndoManager= DocumentUndoManagerRegistry.getDocumentUndoManager(newDocument);
533 									if (newUndoManager != null)
534 										newUndoManager.transferUndoHistory(previousUndoManager);
535 								}
536 								previousUndoManager.disconnect(this);
537 							}
538 
539 							if (wasDirty && changed != null) {
540 								Runnable r2 = () -> {
541 									validateState(getEditorInput());
542 									d.getDocument(getEditorInput()).set(previousContent);
543 									updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
544 									restoreSelection();
545 								};
546 								execute(r2, doValidationAsync);
547 							} else
548 								restoreSelection();
549 
550 						}
551 					}
552 				};
553 				execute(r, false);
554 			}
555 		}
556 
557 		@Override
elementStateChanging(Object element)558 		public void elementStateChanging(Object element) {
559 			if (element != null && element.equals(getEditorInput()))
560 				enableSanityChecking(false);
561 		}
562 
563 		@Override
elementStateChangeFailed(Object element)564 		public void elementStateChangeFailed(Object element) {
565 			if (element != null && element.equals(getEditorInput()))
566 				enableSanityChecking(true);
567 		}
568 
569 		/**
570 		 * Executes the given runnable in the UI thread.
571 		 * <p>
572 		 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76765 for details
573 		 * about why the parameter <code>postAsync</code> has been
574 		 * introduced in the course of 3.1.
575 		 *
576 		 * @param runnable runnable to be executed
577 		 * @param postAsync <code>true</code> if the runnable must be posted asynchronous, <code>false</code> otherwise
578 		 * @since 3.0
579 		 */
execute(Runnable runnable, boolean postAsync)580 		private void execute(Runnable runnable, boolean postAsync) {
581 			if (postAsync || Display.getCurrent() == null) {
582 				if (fDisplay == null)
583 					fDisplay= getSite().getShell().getDisplay();
584 				fDisplay.asyncExec(runnable);
585 			} else
586 				runnable.run();
587 		}
588 	}
589 
590 	/**
591 	 * Internal text listener for updating all content dependent
592 	 * actions. The updating is done asynchronously.
593 	 */
594 	class TextListener implements ITextListener, ITextInputListener {
595 
596 		/**
597 		 * Should the last edit position be updated?
598 		 *
599 		 * @since 3.0
600 		 */
601 		private boolean fUpdateLastEditPosition = false;
602 
603 		/**
604 		 * The editor's last edit position
605 		 *
606 		 * @since 3.0
607 		 */
608 		private Position fLocalLastEditPosition;
609 
610 		/** The posted updater code. */
611 		private Runnable fRunnable = () -> {
612 			fIsRunnablePosted = false;
613 
614 			if (fSourceViewer != null) {
615 				updateContentDependentActions();
616 
617 				// remember the last edit position
618 				if (isDirty() && fUpdateLastEditPosition) {
619 					fUpdateLastEditPosition = false;
620 					ISelection sel = getSelectionProvider().getSelection();
621 					IEditorInput input = getEditorInput();
622 					IDocument document = getDocumentProvider().getDocument(input);
623 
624 					if (fLocalLastEditPosition != null) {
625 						if (document != null) {
626 							document.removePosition(fLocalLastEditPosition);
627 						}
628 						fLocalLastEditPosition = null;
629 					}
630 
631 					if (sel instanceof ITextSelection && !sel.isEmpty()) {
632 						ITextSelection s = (ITextSelection) sel;
633 						fLocalLastEditPosition = new Position(s.getOffset(), s.getLength());
634 						if (document != null) {
635 							try {
636 								document.addPosition(fLocalLastEditPosition);
637 							} catch (BadLocationException ex) {
638 								fLocalLastEditPosition = null;
639 							}
640 						}
641 					}
642 
643 					IEditorSite editorSite = getEditorSite();
644 					if (editorSite instanceof MultiPageEditorSite)
645 						editorSite = ((MultiPageEditorSite) editorSite).getMultiPageEditor().getEditorSite();
646 					TextEditorPlugin.getDefault()
647 							.setLastEditPosition(new EditPosition(input, editorSite.getId(), fLocalLastEditPosition));
648 				}
649 			}
650 		};
651 
652 		/** Display used for posting the updater code. */
653 		private Display fDisplay;
654 
655 		/**
656 		 * Has the runnable been posted?
657 		 * @since 3.0
658 		 */
659 		private boolean fIsRunnablePosted= false;
660 
661 		@Override
textChanged(TextEvent event)662 		public void textChanged(TextEvent event) {
663 
664 			/*
665 			 * Also works for text events which do not base on a DocumentEvent.
666 			 * This way, if the visible document of the viewer changes, all content
667 			 * dependent actions are updated as well.
668 			 */
669 
670 			if (fDisplay == null)
671 				fDisplay= getSite().getShell().getDisplay();
672 
673 			if (event.getDocumentEvent() != null)
674 				fUpdateLastEditPosition= true;
675 
676 			if (!fIsRunnablePosted) {
677 				fIsRunnablePosted= true;
678 				fDisplay.asyncExec(fRunnable);
679 			}
680 		}
681 
682 		@Override
inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput)683 		public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
684 			if (oldInput != null && fLocalLastEditPosition != null) {
685 				oldInput.removePosition(fLocalLastEditPosition);
686 				fLocalLastEditPosition= null;
687 			}
688 		}
689 
690 		@Override
inputDocumentChanged(IDocument oldInput, IDocument newInput)691 		public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
692 		}
693 	}
694 
695 	/**
696 	 * Internal property change listener for handling changes in the editor's preferences.
697 	 */
698 	class PropertyChangeListener implements IPropertyChangeListener {
699 		@Override
propertyChange(PropertyChangeEvent event)700 		public void propertyChange(PropertyChangeEvent event) {
701 			handlePreferenceStoreChanged(event);
702 		}
703 	}
704 
705 	/**
706 	 * Internal property change listener for handling workbench font changes.
707 	 * @since 2.1
708 	 */
709 	class FontPropertyChangeListener implements IPropertyChangeListener {
710 		@Override
propertyChange(PropertyChangeEvent event)711 		public void propertyChange(PropertyChangeEvent event) {
712 			if (fSourceViewer == null)
713 				return;
714 
715 			String property= event.getProperty();
716 
717 			// IMPORTANT: Do not call isBlockSelectionModeEnabled() before checking the property!
718 
719 			if (BLOCK_SELECTION_MODE_FONT.equals(property) && isBlockSelectionModeEnabled()) {
720 				Font blockFont= JFaceResources.getFont(BLOCK_SELECTION_MODE_FONT);
721 				setFont(fSourceViewer, blockFont);
722 				disposeFont();
723 				updateCaret();
724 				return;
725 			}
726 			if (getFontPropertyPreferenceKey().equals(property) && !isBlockSelectionModeEnabled()) {
727 				initializeViewerFont(fSourceViewer);
728 				updateCaret();
729 				return;
730 			}
731 		}
732 	}
733 
734 	/**
735 	 * Internal key verify listener for triggering action activation codes.
736 	 */
737 	class ActivationCodeTrigger implements VerifyKeyListener {
738 
739 		/** Indicates whether this trigger has been installed. */
740 		private boolean fIsInstalled= false;
741 		/**
742 		 * The key binding service to use.
743 		 * @since 2.0
744 		 */
745 		private IKeyBindingService fKeyBindingService;
746 
747 		@Override
verifyKey(VerifyEvent event)748 		public void verifyKey(VerifyEvent event) {
749 
750 			ActionActivationCode code= null;
751 			int size= fActivationCodes.size();
752 			for (int i= 0; i < size; i++) {
753 				code= fActivationCodes.get(i);
754 				if (code.matches(event)) {
755 					IAction action= getAction(code.fActionId);
756 					if (action != null) {
757 
758 						if (action instanceof IUpdate)
759 							((IUpdate) action).update();
760 
761 						if (!action.isEnabled() && action instanceof IReadOnlyDependent) {
762 							IReadOnlyDependent dependent= (IReadOnlyDependent) action;
763 							boolean writable= dependent.isEnabled(true);
764 							if (writable) {
765 								event.doit= false;
766 								return;
767 							}
768 						} else if (action.isEnabled()) {
769 							event.doit= false;
770 							action.run();
771 							return;
772 						}
773 					}
774 				}
775 			}
776 		}
777 
778 		/**
779 		 * Installs this trigger on the editor's text widget.
780 		 * @since 2.0
781 		 */
install()782 		public void install() {
783 			if (!fIsInstalled) {
784 
785 				if (fSourceViewer instanceof ITextViewerExtension) {
786 					ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
787 					e.prependVerifyKeyListener(this);
788 				} else {
789 					StyledText text= fSourceViewer.getTextWidget();
790 					text.addVerifyKeyListener(this);
791 				}
792 
793 				fKeyBindingService= getEditorSite().getKeyBindingService();
794 				fIsInstalled= true;
795 			}
796 		}
797 
798 		/**
799 		 * Uninstalls this trigger from the editor's text widget.
800 		 * @since 2.0
801 		 */
uninstall()802 		public void uninstall() {
803 			if (fIsInstalled) {
804 
805 				if (fSourceViewer instanceof ITextViewerExtension) {
806 					ITextViewerExtension e= (ITextViewerExtension) fSourceViewer;
807 					e.removeVerifyKeyListener(this);
808 				} else if (fSourceViewer != null) {
809 					StyledText text= fSourceViewer.getTextWidget();
810 					if (text != null && !text.isDisposed())
811 						text.removeVerifyKeyListener(fActivationCodeTrigger);
812 				}
813 
814 				fIsInstalled= false;
815 				fKeyBindingService= null;
816 			}
817 		}
818 
819 		/**
820 		 * Registers the given action for key activation.
821 		 * @param action the action to be registered
822 		 * @since 2.0
823 		 */
registerActionForKeyActivation(IAction action)824 		public void registerActionForKeyActivation(IAction action) {
825 			if (fIsInstalled && action.getActionDefinitionId() != null)
826 				fKeyBindingService.registerAction(action);
827 		}
828 
829 		/**
830 		 * The given action is no longer available for key activation
831 		 * @param action the action to be unregistered
832 		 * @since 2.0
833 		 */
unregisterActionFromKeyActivation(IAction action)834 		public void unregisterActionFromKeyActivation(IAction action) {
835 			if (fIsInstalled && action.getActionDefinitionId() != null)
836 				fKeyBindingService.unregisterAction(action);
837 		}
838 
839 		/**
840 		 * Sets the key binding scopes for this editor.
841 		 * @param keyBindingScopes the key binding scopes
842 		 * @since 2.1
843 		 */
setScopes(String[] keyBindingScopes)844 		public void setScopes(String[] keyBindingScopes) {
845 			if (keyBindingScopes != null && keyBindingScopes.length > 0)
846 				fKeyBindingService.setScopes(keyBindingScopes);
847 		}
848 	}
849 
850 	/**
851 	 * Representation of action activation codes.
852 	 */
853 	static class ActionActivationCode {
854 
855 		/** The action id. */
856 		public String fActionId;
857 		/** The character. */
858 		public char fCharacter;
859 		/** The key code. */
860 		public int fKeyCode= -1;
861 		/** The state mask. */
862 		public int fStateMask= SWT.DEFAULT;
863 
864 		/**
865 		 * Creates a new action activation code for the given action id.
866 		 * @param actionId the action id
867 		 */
ActionActivationCode(String actionId)868 		public ActionActivationCode(String actionId) {
869 			fActionId= actionId;
870 		}
871 
872 		/**
873 		 * Returns <code>true</code> if this activation code matches the given verify event.
874 		 * @param event the event to test for matching
875 		 * @return whether this activation code matches <code>event</code>
876 		 */
matches(VerifyEvent event)877 		public boolean matches(VerifyEvent event) {
878 			return (event.character == fCharacter &&
879 						(fKeyCode == -1 || event.keyCode == fKeyCode) &&
880 						(fStateMask == SWT.DEFAULT || event.stateMask == fStateMask));
881 		}
882 	}
883 
884 	/**
885 	 * Internal part and shell activation listener for triggering state validation.
886 	 * @since 2.0
887 	 */
888 	class ActivationListener implements IPartListener, IWindowListener {
889 
890 		/** Cache of the active workbench part. */
891 		private IWorkbenchPart fActivePart;
892 		/**
893 		 * The part service.
894 		 * @since 3.1
895 		 */
896 		private IPartService fPartService;
897 
898 		/**
899 		 * Creates this activation listener.
900 		 *
901 		 * @param partService the part service on which to add the part listener
902 		 * @since 3.1
903 		 */
ActivationListener(IPartService partService)904 		public ActivationListener(IPartService partService) {
905 			fPartService= partService;
906 			fPartService.addPartListener(this);
907 			PlatformUI.getWorkbench().addWindowListener(this);
908 		}
909 
910 		/**
911 		 * Disposes this activation listener.
912 		 *
913 		 * @since 3.1
914 		 */
dispose()915 		public void dispose() {
916 			fPartService.removePartListener(this);
917 			PlatformUI.getWorkbench().removeWindowListener(this);
918 			fPartService= null;
919 		}
920 
921 		@Override
partActivated(IWorkbenchPart part)922 		public void partActivated(IWorkbenchPart part) {
923 			fActivePart= part;
924 			handleActivation();
925 		}
926 
927 		@Override
partBroughtToTop(IWorkbenchPart part)928 		public void partBroughtToTop(IWorkbenchPart part) {
929 		}
930 
931 		@Override
partClosed(IWorkbenchPart part)932 		public void partClosed(IWorkbenchPart part) {
933 		}
934 
935 		@Override
partDeactivated(IWorkbenchPart part)936 		public void partDeactivated(IWorkbenchPart part) {
937 			fActivePart= null;
938 		}
939 
940 		@Override
partOpened(IWorkbenchPart part)941 		public void partOpened(IWorkbenchPart part) {
942 			// Restore the saved state if any
943 			if ((part == AbstractTextEditor.this || part.getAdapter(AbstractTextEditor.class) == AbstractTextEditor.this) && fMementoToRestore != null && containsSavedState(fMementoToRestore)) {
944 				doRestoreState(fMementoToRestore);
945 				fMementoToRestore= null;
946 			}
947 		}
948 
949 		/**
950 		 * Handles the activation triggering a element state check in the editor.
951 		 */
handleActivation()952 		private void handleActivation() {
953 			if (!fHandleActivation)
954 				return;
955 
956 			if (fActivePart == AbstractTextEditor.this || fActivePart != null && fActivePart.getAdapter(AbstractTextEditor.class) == AbstractTextEditor.this) {
957 				fHandleActivation= false;
958 				try {
959 					safelySanityCheckState(getEditorInput());
960 				} finally {
961 					fHandleActivation= true;
962 					fHasBeenActivated= true;
963 				}
964 			}
965 		}
966 
967 		@Override
windowActivated(IWorkbenchWindow window)968 		public void windowActivated(IWorkbenchWindow window) {
969 			if (fHandleActivation && window == getEditorSite().getWorkbenchWindow()) {
970 				/*
971 				 * Workaround for problem described in
972 				 * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731
973 				 * Will be removed when SWT has solved the problem.
974 				 */
975 				window.getShell().getDisplay().asyncExec(this::handleActivation);
976 			}
977 		}
978 
979 		@Override
windowDeactivated(IWorkbenchWindow window)980 		public void windowDeactivated(IWorkbenchWindow window) {
981 		}
982 
983 		@Override
windowClosed(IWorkbenchWindow window)984 		public void windowClosed(IWorkbenchWindow window) {
985 		}
986 
987 		@Override
windowOpened(IWorkbenchWindow window)988 		public void windowOpened(IWorkbenchWindow window) {
989 		}
990 	}
991 
992 	/**
993 	 * Internal interface for a cursor listener. I.e. aggregation
994 	 * of mouse and key listener.
995 	 * @since 2.0
996 	 */
997 	interface ICursorListener extends MouseListener, KeyListener {
998 	}
999 
1000 	/**
1001 	 * Maps an action definition id to an StyledText action.
1002 	 * @since 2.0
1003 	 */
1004 	protected static final class IdMapEntry {
1005 
1006 		/** The action id. */
1007 		private String fActionId;
1008 		/** The StyledText action. */
1009 		private int fAction;
1010 
1011 		/**
1012 		 * Creates a new mapping.
1013 		 * @param actionId the action id
1014 		 * @param action the StyledText action
1015 		 */
IdMapEntry(String actionId, int action)1016 		public IdMapEntry(String actionId, int action) {
1017 			fActionId= actionId;
1018 			fAction= action;
1019 		}
1020 
1021 		/**
1022 		 * Returns the action id.
1023 		 * @return the action id
1024 		 */
getActionId()1025 		public String getActionId() {
1026 			return fActionId;
1027 		}
1028 
1029 		/**
1030 		 * Returns the action.
1031 		 * @return the action
1032 		 */
getAction()1033 		public int getAction() {
1034 			return fAction;
1035 		}
1036 	}
1037 
1038 	/**
1039 	 * Internal action to scroll the editor's viewer by a specified number of lines.
1040 	 * @since 2.0
1041 	 */
1042 	class ScrollLinesAction extends Action {
1043 
1044 		/** Number of lines to scroll. */
1045 		private int fScrollIncrement;
1046 
1047 		/**
1048 		 * Creates a new scroll action that scroll the given number of lines. If the
1049 		 * increment is &lt; 0, it's scrolling up, if &gt; 0 it's scrolling down.
1050 		 * @param scrollIncrement the number of lines to scroll
1051 		 */
ScrollLinesAction(int scrollIncrement)1052 		public ScrollLinesAction(int scrollIncrement) {
1053 			fScrollIncrement= scrollIncrement;
1054 		}
1055 
1056 		@Override
run()1057 		public void run() {
1058 			if (fSourceViewer instanceof ITextViewerExtension5) {
1059 				ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
1060 				StyledText textWidget= fSourceViewer.getTextWidget();
1061 				int topIndex= textWidget.getTopIndex();
1062 				int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
1063 				fSourceViewer.setTopIndex(extension.widgetLine2ModelLine(newTopIndex));
1064 			} else {
1065 				int topIndex= fSourceViewer.getTopIndex();
1066 				int newTopIndex= Math.max(0, topIndex + fScrollIncrement);
1067 				fSourceViewer.setTopIndex(newTopIndex);
1068 			}
1069 		}
1070 	}
1071 
1072 	/**
1073 	 * Action to toggle the insert mode. The action is checked if smart mode is
1074 	 * turned on.
1075 	 *
1076 	 * @since 2.1
1077 	 */
1078 	class ToggleInsertModeAction extends ResourceAction {
1079 
ToggleInsertModeAction(ResourceBundle bundle, String prefix)1080 		public ToggleInsertModeAction(ResourceBundle bundle, String prefix) {
1081 			super(bundle, prefix, IAction.AS_CHECK_BOX);
1082 		}
1083 
1084 		@Override
run()1085 		public void run() {
1086 			switchToNextInsertMode();
1087 		}
1088 
1089 		@Override
isChecked()1090 		public boolean isChecked() {
1091 			return fInsertMode == SMART_INSERT;
1092 		}
1093 	}
1094 
1095 	/**
1096 	 * Action to toggle the overwrite mode.
1097 	 *
1098 	 * @since 3.0
1099 	 */
1100 	class ToggleOverwriteModeAction extends ResourceAction {
1101 
ToggleOverwriteModeAction(ResourceBundle bundle, String prefix)1102 		public ToggleOverwriteModeAction(ResourceBundle bundle, String prefix) {
1103 			super(bundle, prefix);
1104 		}
1105 
1106 		@Override
run()1107 		public void run() {
1108 			toggleOverwriteMode();
1109 		}
1110 	}
1111 
1112 	/**
1113 	 * This action implements smart end.
1114 	 * Instead of going to the end of a line it does the following:
1115 	 * - if smart home/end is enabled and the caret is before the line's last non-whitespace and then the caret is moved directly after it
1116 	 * - if the caret is after last non-whitespace the caret is moved at the end of the line
1117 	 * - if the caret is at the end of the line the caret is moved directly after the line's last non-whitespace character
1118 	 * @since 2.1 (in 3.3 the access modifier changed from package visibility to protected)
1119 	 */
1120 	protected class LineEndAction extends TextNavigationAction {
1121 
1122 		/** boolean flag which tells if the text up to the line end should be selected. */
1123 		private boolean fDoSelect;
1124 
1125 		/**
1126 		 * Create a new line end action.
1127 		 *
1128 		 * @param textWidget the styled text widget
1129 		 * @param doSelect a boolean flag which tells if the text up to the line end should be selected
1130 		 */
LineEndAction(StyledText textWidget, boolean doSelect)1131 		public LineEndAction(StyledText textWidget, boolean doSelect) {
1132 			super(textWidget, ST.LINE_END);
1133 			fDoSelect= doSelect;
1134 		}
1135 
1136 		/**
1137 		 * Computes the offset of the line end position.
1138 		 *
1139 		 * @param document the document where to compute the line end position
1140 		 * @param line the line to determine the end position of
1141 		 * @param length the length of the line
1142 		 * @param offset the caret position in the document
1143 		 * @return the offset of the line end
1144 		 * @since 3.4 protected, was added in 3.3 as private method
1145 		 */
getLineEndPosition(final IDocument document, final String line, final int length, final int offset)1146 		protected int getLineEndPosition(final IDocument document, final String line, final int length, final int offset) {
1147 			int index= length - 1;
1148 			while (index > -1 && Character.isWhitespace(line.charAt(index)))
1149 				index--;
1150 			index++;
1151 
1152 			LinkedModeModel model= LinkedModeModel.getModel(document, offset);
1153 			if (model != null) {
1154 				LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
1155 				if (linkedPosition != null) {
1156 					int linkedPositionEnd= linkedPosition.getOffset() + linkedPosition.getLength();
1157 					int lineOffset;
1158 					try {
1159 						lineOffset= document.getLineInformationOfOffset(offset).getOffset();
1160 						if (offset != linkedPositionEnd && linkedPositionEnd - lineOffset < index)
1161 							index= linkedPositionEnd - lineOffset;
1162 					} catch (BadLocationException e) {
1163 						//should not happen
1164 					}
1165 				}
1166 			}
1167 			return index;
1168 		}
1169 
1170 		@Override
run()1171 		public void run() {
1172 			boolean isSmartHomeEndEnabled= false;
1173 			IPreferenceStore store= getPreferenceStore();
1174 			if (store != null)
1175 				isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);
1176 
1177 			StyledText st= fSourceViewer.getTextWidget();
1178 			if (st == null || st.isDisposed())
1179 				return;
1180 			int caretOffset= st.getCaretOffset();
1181 			int lineNumber= st.getLineAtOffset(caretOffset);
1182 			int lineOffset= st.getOffsetAtLine(lineNumber);
1183 
1184 			int lineLength;
1185 			int caretOffsetInDocument;
1186 			final IDocument document= fSourceViewer.getDocument();
1187 
1188 			try {
1189 				caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
1190 				lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
1191 			} catch (BadLocationException ex) {
1192 				return;
1193 			}
1194 			int lineEndOffset= lineOffset + lineLength;
1195 
1196 			int delta= lineEndOffset - st.getCharCount();
1197 			if (delta > 0) {
1198 				lineEndOffset -= delta;
1199 				lineLength -= delta;
1200 			}
1201 
1202 			String line= ""; //$NON-NLS-1$
1203 			if (lineLength > 0)
1204 				line= st.getText(lineOffset, lineEndOffset - 1);
1205 
1206 			// Remember current selection
1207 			Point oldSelection= st.getSelection();
1208 
1209 			// The new caret position
1210 			int newCaretOffset= -1;
1211 
1212 			if (isSmartHomeEndEnabled) {
1213 				// Compute the line end offset
1214 				int i= getLineEndPosition(document, line, lineLength, caretOffsetInDocument);
1215 
1216 				if (caretOffset - lineOffset == i)
1217 					// to end of line
1218 					newCaretOffset= lineEndOffset;
1219 				else
1220 					// to end of text
1221 					newCaretOffset= lineOffset + i;
1222 
1223 			} else {
1224 
1225 				if (caretOffset < lineEndOffset)
1226 					// to end of line
1227 					newCaretOffset= lineEndOffset;
1228 
1229 			}
1230 
1231 			if (newCaretOffset == -1)
1232 				newCaretOffset= caretOffset;
1233 			else
1234 				st.setCaretOffset(newCaretOffset);
1235 
1236 			st.setCaretOffset(newCaretOffset);
1237 			if (fDoSelect) {
1238 				if (caretOffset < oldSelection.y)
1239 					st.setSelection(oldSelection.y, newCaretOffset);
1240 				else
1241 					st.setSelection(oldSelection.x, newCaretOffset);
1242 			} else
1243 				st.setSelection(newCaretOffset);
1244 
1245 			fireSelectionChanged(oldSelection);
1246 		}
1247 	}
1248 
1249 	/**
1250 	 * This action implements smart home.
1251 	 * Instead of going to the start of a line it does the following:
1252 	 * - if smart home/end is enabled and the caret is after the line's first non-whitespace then the caret is moved directly before it
1253 	 * - if the caret is before the line's first non-whitespace the caret is moved to the beginning of the line
1254 	 * - if the caret is at the beginning of the line the caret is moved directly before the line's first non-whitespace character
1255 	 * @since 2.1
1256 	 */
1257 	protected class LineStartAction extends TextNavigationAction {
1258 
1259 		/** boolean flag which tells if the text up to the beginning of the line should be selected. */
1260 		private final boolean fDoSelect;
1261 
1262 		/**
1263 		 * Creates a new line start action.
1264 		 *
1265 		 * @param textWidget the styled text widget
1266 		 * @param doSelect a boolean flag which tells if the text up to the beginning of the line should be selected
1267 		 */
LineStartAction(final StyledText textWidget, final boolean doSelect)1268 		public LineStartAction(final StyledText textWidget, final boolean doSelect) {
1269 			super(textWidget, ST.LINE_START);
1270 			fDoSelect= doSelect;
1271 		}
1272 
1273 		/**
1274 		 * Computes the offset of the line start position.
1275 		 *
1276 		 * @param document the document where to compute the line start position
1277 		 * @param line the line to determine the start position of
1278 		 * @param length the length of the line
1279 		 * @param offset the caret position in the document
1280 		 * @return the offset of the line start
1281 		 * @since 3.0
1282 		 */
getLineStartPosition(final IDocument document, final String line, final int length, final int offset)1283 		protected int getLineStartPosition(final IDocument document, final String line, final int length, final int offset) {
1284 			int index= 0;
1285 			while (index < length && Character.isWhitespace(line.charAt(index)))
1286 				index++;
1287 
1288 			LinkedModeModel model= LinkedModeModel.getModel(document, offset);
1289 			if (model != null) {
1290 				LinkedPosition linkedPosition= model.findPosition(new LinkedPosition(document, offset, 0));
1291 				if (linkedPosition != null) {
1292 					int linkedPositionOffset= linkedPosition.getOffset();
1293 					int lineOffset;
1294 					try {
1295 						lineOffset= document.getLineInformationOfOffset(offset).getOffset();
1296 						if (offset != linkedPositionOffset && index < linkedPositionOffset - lineOffset)
1297 							index= linkedPositionOffset - lineOffset;
1298 					} catch (BadLocationException e) {
1299 						//should not happen
1300 					}
1301 				}
1302 			}
1303 			return index;
1304 		}
1305 
1306 		@Override
run()1307 		public void run() {
1308 			boolean isSmartHomeEndEnabled= false;
1309 			IPreferenceStore store= getPreferenceStore();
1310 			if (store != null)
1311 				isSmartHomeEndEnabled= store.getBoolean(AbstractTextEditor.PREFERENCE_NAVIGATION_SMART_HOME_END);
1312 
1313 			StyledText st= fSourceViewer.getTextWidget();
1314 			if (st == null || st.isDisposed())
1315 				return;
1316 
1317 			int caretOffset= st.getCaretOffset();
1318 			int lineNumber= st.getLineAtOffset(caretOffset);
1319 			int lineOffset= st.getOffsetAtLine(lineNumber);
1320 
1321 			int lineLength;
1322 			int caretOffsetInDocument;
1323 			final IDocument document= fSourceViewer.getDocument();
1324 
1325 			try {
1326 				caretOffsetInDocument= widgetOffset2ModelOffset(fSourceViewer, caretOffset);
1327 				lineLength= document.getLineInformationOfOffset(caretOffsetInDocument).getLength();
1328 			} catch (BadLocationException ex) {
1329 				return;
1330 			}
1331 
1332 			String line= ""; //$NON-NLS-1$
1333 			if (lineLength > 0) {
1334 				int end= lineOffset + lineLength - 1;
1335 				end= Math.min(end, st.getCharCount() -1);
1336 				line= st.getText(lineOffset, end);
1337 			}
1338 
1339 			// Remember current selection
1340 			Point oldSelection= st.getSelection();
1341 
1342 			// The new caret position
1343 			int newCaretOffset= -1;
1344 
1345 			if (isSmartHomeEndEnabled) {
1346 
1347 				// Compute the line start offset
1348 				int index= getLineStartPosition(document, line, lineLength, caretOffsetInDocument);
1349 
1350 				if (caretOffset - lineOffset == index)
1351 					// to beginning of line
1352 					newCaretOffset= lineOffset;
1353 				else
1354 					// to beginning of text
1355 					newCaretOffset= lineOffset + index;
1356 
1357 			} else {
1358 
1359 				if (caretOffset > lineOffset)
1360 					// to beginning of line
1361 					newCaretOffset= lineOffset;
1362 			}
1363 
1364 			if (newCaretOffset == -1)
1365 				newCaretOffset= caretOffset;
1366 			else
1367 				st.setCaretOffset(newCaretOffset);
1368 
1369 			if (fDoSelect) {
1370 				if (caretOffset < oldSelection.y)
1371 					st.setSelection(oldSelection.y, newCaretOffset);
1372 				else
1373 					st.setSelection(oldSelection.x, newCaretOffset);
1374 			} else
1375 				st.setSelection(newCaretOffset);
1376 
1377 			fireSelectionChanged(oldSelection);
1378 		}
1379 
1380 	}
1381 
1382 	/**
1383 	 * Internal action to show the editor's ruler context menu (accessibility).
1384 	 * @since 2.0
1385 	 */
1386 	class ShowRulerContextMenuAction extends Action {
1387 		@Override
run()1388 		public void run() {
1389 			if (fSourceViewer == null)
1390 				return;
1391 
1392 			StyledText text= fSourceViewer.getTextWidget();
1393 			if (text == null || text.isDisposed())
1394 				return;
1395 
1396 			Point location= text.getLocationAtOffset(text.getCaretOffset());
1397 			location.x= 0;
1398 
1399 			if (fVerticalRuler instanceof IVerticalRulerExtension)
1400 				((IVerticalRulerExtension) fVerticalRuler).setLocationOfLastMouseButtonActivity(location.x, location.y);
1401 
1402 			location= text.toDisplay(location);
1403 			fRulerContextMenu.setLocation(location.x, location.y);
1404 			fRulerContextMenu.setVisible(true);
1405 		}
1406 	}
1407 
1408 	/**
1409 	 * Editor specific selection provider which wraps the source viewer's selection provider.
1410 	 *
1411 	 * @since 3.4 protected, was added in 2.1 as private class
1412 	 */
1413 	protected class SelectionProvider implements IPostSelectionProvider, ISelectionValidator {
1414 
1415 		@Override
addSelectionChangedListener(ISelectionChangedListener listener)1416 		public void addSelectionChangedListener(ISelectionChangedListener listener) {
1417 			if (fSourceViewer != null)
1418 				fSourceViewer.getSelectionProvider().addSelectionChangedListener(listener);
1419 		}
1420 
1421 		@Override
getSelection()1422 		public ISelection getSelection() {
1423 			return doGetSelection();
1424 		}
1425 
1426 		@Override
removeSelectionChangedListener(ISelectionChangedListener listener)1427 		public void removeSelectionChangedListener(ISelectionChangedListener listener) {
1428 			if (fSourceViewer != null)
1429 				fSourceViewer.getSelectionProvider().removeSelectionChangedListener(listener);
1430 		}
1431 
1432 		@Override
setSelection(ISelection selection)1433 		public void setSelection(ISelection selection) {
1434 			doSetSelection(selection);
1435 		}
1436 
1437 		@Override
addPostSelectionChangedListener(ISelectionChangedListener listener)1438 		public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
1439 			if (fSourceViewer != null) {
1440 				if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider)  {
1441 					IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
1442 					provider.addPostSelectionChangedListener(listener);
1443 				}
1444 			}
1445 		}
1446 
1447 		@Override
removePostSelectionChangedListener(ISelectionChangedListener listener)1448 		public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
1449 			if (fSourceViewer != null)  {
1450 				if (fSourceViewer.getSelectionProvider() instanceof IPostSelectionProvider)  {
1451 					IPostSelectionProvider provider= (IPostSelectionProvider) fSourceViewer.getSelectionProvider();
1452 					provider.removePostSelectionChangedListener(listener);
1453 				}
1454 			}
1455 		}
1456 
1457 		@Override
isValid(ISelection postSelection)1458 		public boolean isValid(ISelection postSelection) {
1459 			return fSelectionListener != null && fSelectionListener.isValid(postSelection);
1460 		}
1461 	}
1462 
1463 	/**
1464 	 * Internal implementation class for a change listener.
1465 	 * @since 3.0
1466 	 */
1467 	protected abstract class AbstractSelectionChangedListener implements ISelectionChangedListener  {
1468 
1469 		/**
1470 		 * Installs this selection changed listener with the given selection provider. If the
1471 		 * selection provider is a post selection provider, post selection changed events are the
1472 		 * preferred choice, otherwise normal selection changed events are requested.
1473 		 *
1474 		 * @param selectionProvider the selection provider
1475 		 */
install(ISelectionProvider selectionProvider)1476 		public void install(ISelectionProvider selectionProvider) {
1477 			if (selectionProvider == null)
1478 				return;
1479 
1480 			if (selectionProvider instanceof IPostSelectionProvider)  {
1481 				IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
1482 				provider.addPostSelectionChangedListener(this);
1483 			} else  {
1484 				selectionProvider.addSelectionChangedListener(this);
1485 			}
1486 		}
1487 
1488 		/**
1489 		 * Removes this selection changed listener from the given selection provider.
1490 		 *
1491 		 * @param selectionProvider the selection provider
1492 		 */
uninstall(ISelectionProvider selectionProvider)1493 		public void uninstall(ISelectionProvider selectionProvider) {
1494 			if (selectionProvider == null)
1495 				return;
1496 
1497 			if (selectionProvider instanceof IPostSelectionProvider)  {
1498 				IPostSelectionProvider provider= (IPostSelectionProvider) selectionProvider;
1499 				provider.removePostSelectionChangedListener(this);
1500 			} else  {
1501 				selectionProvider.removeSelectionChangedListener(this);
1502 			}
1503 		}
1504 	}
1505 
1506 	/**
1507 	 * This selection listener allows the SelectionProvider to implement {@link ISelectionValidator}.
1508 	 *
1509 	 * @since 3.0
1510 	 */
1511 	private class SelectionListener extends AbstractSelectionChangedListener implements IDocumentListener {
1512 
1513 		private IDocument fDocument;
1514 		private final Object INVALID_SELECTION= new Object();
1515 		private Object fPostSelection= INVALID_SELECTION;
1516 
1517 		@Override
selectionChanged(SelectionChangedEvent event)1518 		public synchronized void selectionChanged(SelectionChangedEvent event) {
1519 			fPostSelection= event.getSelection();
1520 		}
1521 
1522 		@Override
documentAboutToBeChanged(DocumentEvent event)1523 		public synchronized void documentAboutToBeChanged(DocumentEvent event) {
1524 			fPostSelection= INVALID_SELECTION;
1525 		}
1526 
1527 		@Override
documentChanged(DocumentEvent event)1528 		public void documentChanged(DocumentEvent event) {
1529 		}
1530 
isValid(ISelection selection)1531 		public synchronized boolean isValid(ISelection selection) {
1532 			return fPostSelection != INVALID_SELECTION && fPostSelection == selection;
1533 		}
1534 
setDocument(IDocument document)1535 		public void setDocument(IDocument document) {
1536 			if (fDocument != null)
1537 				fDocument.removeDocumentListener(this);
1538 
1539 			fDocument= document;
1540 			if (fDocument != null)
1541 				fDocument.addDocumentListener(this);
1542 		}
1543 
1544 		@Override
install(ISelectionProvider selectionProvider)1545 		public void install(ISelectionProvider selectionProvider) {
1546 			super.install(selectionProvider);
1547 
1548 			if (selectionProvider != null)
1549 				selectionProvider.addSelectionChangedListener(this);
1550 		}
1551 
1552 		@Override
uninstall(ISelectionProvider selectionProvider)1553 		public void uninstall(ISelectionProvider selectionProvider) {
1554 			if (selectionProvider != null)
1555 				selectionProvider.removeSelectionChangedListener(this);
1556 
1557 			if (fDocument != null) {
1558 				fDocument.removeDocumentListener(this);
1559 				fDocument= null;
1560 			}
1561 			super.uninstall(selectionProvider);
1562 		}
1563 	}
1564 
1565 
1566 	/**
1567 	 * Implements the ruler column support of for the given editor.
1568 	 * <p>
1569 	 * This is currently only used to support vertical ruler columns.
1570 	 * </p>
1571 	 *
1572 	 * @since 3.3
1573 	 */
1574 	protected static class ColumnSupport implements IColumnSupport {
1575 		private final AbstractTextEditor fEditor;
1576 		private final RulerColumnRegistry fRegistry;
1577 		private final List<IContributedRulerColumn> fColumns;
1578 
1579 		/**
1580 		 * Creates a new column support for the given editor. Only the editor itself should normally
1581 		 * create such an instance.
1582 		 *
1583 		 * @param editor the editor
1584 		 * @param registry the contribution registry to refer to
1585 		 */
ColumnSupport(AbstractTextEditor editor, RulerColumnRegistry registry)1586 		public ColumnSupport(AbstractTextEditor editor, RulerColumnRegistry registry) {
1587 			Assert.isLegal(editor != null);
1588 			Assert.isLegal(registry != null);
1589 			fEditor= editor;
1590 			fRegistry= registry;
1591 			fColumns= new ArrayList<>();
1592 		}
1593 
1594 		@Override
setColumnVisible(RulerColumnDescriptor descriptor, boolean visible)1595 		public final void setColumnVisible(RulerColumnDescriptor descriptor, boolean visible) {
1596 			Assert.isLegal(descriptor != null);
1597 
1598 			final CompositeRuler ruler= getRuler();
1599 			if (ruler == null)
1600 				return;
1601 
1602 			if (!isColumnSupported(descriptor))
1603 				visible= false;
1604 
1605 			if (isColumnVisible(descriptor)) {
1606 				if (!visible)
1607 					removeColumn(ruler, descriptor);
1608 			} else {
1609 				if (visible)
1610 					addColumn(ruler, descriptor);
1611 			}
1612 		}
1613 
addColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor)1614 		private void addColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
1615 
1616 			final int idx= computeIndex(ruler, descriptor);
1617 
1618 			SafeRunnable runnable= new SafeRunnable() {
1619 				@Override
1620 				public void run() throws Exception {
1621 					IContributedRulerColumn column= descriptor.createColumn(fEditor);
1622 					fColumns.add(column);
1623 					initializeColumn(column);
1624 					ruler.addDecorator(idx, column);
1625 				}
1626 			};
1627 			SafeRunner.run(runnable);
1628 		}
1629 
1630 		/**
1631 		 * Hook to let subclasses initialize a newly created column.
1632 		 * <p>
1633 		 * Subclasses may extend this method.</p>
1634 		 *
1635 		 * @param column the created column
1636 		 */
initializeColumn(IContributedRulerColumn column)1637 		protected void initializeColumn(IContributedRulerColumn column) {
1638 		}
1639 
removeColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor)1640 		private void removeColumn(final CompositeRuler ruler, final RulerColumnDescriptor descriptor) {
1641 			removeColumn(ruler, getVisibleColumn(ruler, descriptor));
1642 		}
1643 
removeColumn(final CompositeRuler ruler, final IContributedRulerColumn rulerColumn)1644 		private void removeColumn(final CompositeRuler ruler, final IContributedRulerColumn rulerColumn) {
1645 			if (rulerColumn != null) {
1646 				SafeRunnable runnable= new SafeRunnable() {
1647 					@Override
1648 					public void run() throws Exception {
1649 						if (ruler != null)
1650 							ruler.removeDecorator(rulerColumn);
1651 						rulerColumn.columnRemoved();
1652 					}
1653 				};
1654 				SafeRunner.run(runnable);
1655 			}
1656 		}
1657 
1658 		/**
1659 		 * Returns the currently visible column matching <code>id</code>, <code>null</code> if
1660 		 * none.
1661 		 *
1662 		 * @param ruler the composite ruler to scan
1663 		 * @param descriptor the descriptor of the column of interest
1664 		 * @return the matching column or <code>null</code>
1665 		 */
getVisibleColumn(CompositeRuler ruler, RulerColumnDescriptor descriptor)1666 		private IContributedRulerColumn getVisibleColumn(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
1667 			for (Iterator<IVerticalRulerColumn> it= ruler.getDecoratorIterator(); it.hasNext();) {
1668 				IVerticalRulerColumn column= it.next();
1669 				if (column instanceof IContributedRulerColumn) {
1670 					IContributedRulerColumn rulerColumn= (IContributedRulerColumn)column;
1671 					RulerColumnDescriptor rcd= rulerColumn.getDescriptor();
1672 					if (descriptor.equals(rcd))
1673 						return rulerColumn;
1674 				}
1675 			}
1676 			return null;
1677 		}
1678 
1679 		/**
1680 		 * Computes the insertion index for a column contribution into the currently visible columns.
1681 		 *
1682 		 * @param ruler the composite ruler into which to insert the column
1683 		 * @param descriptor the descriptor to compute the index for
1684 		 * @return the insertion index for a new column
1685 		 */
computeIndex(CompositeRuler ruler, RulerColumnDescriptor descriptor)1686 		private int computeIndex(CompositeRuler ruler, RulerColumnDescriptor descriptor) {
1687 			int index= 0;
1688 			List<RulerColumnDescriptor> all= fRegistry.getColumnDescriptors();
1689 			int newPos= all.indexOf(descriptor);
1690 			for (Iterator<IVerticalRulerColumn> it= ruler.getDecoratorIterator(); it.hasNext();) {
1691 				IVerticalRulerColumn column= it.next();
1692 				if (column instanceof IContributedRulerColumn) {
1693 					RulerColumnDescriptor rcd= ((IContributedRulerColumn)column).getDescriptor();
1694 					if (rcd != null && all.indexOf(rcd) > newPos)
1695 						break;
1696 				} else if ("org.eclipse.jface.text.source.projection.ProjectionRulerColumn".equals(column.getClass().getName())) { //$NON-NLS-1$
1697 					// projection column is always the rightmost column
1698 					break;
1699 				}
1700 				index++;
1701 			}
1702 			return index;
1703 		}
1704 
1705 		@Override
isColumnVisible(RulerColumnDescriptor descriptor)1706 		public final boolean isColumnVisible(RulerColumnDescriptor descriptor) {
1707 			Assert.isLegal(descriptor != null);
1708 			CompositeRuler ruler= getRuler();
1709 			return ruler != null && getVisibleColumn(ruler, descriptor) != null;
1710 		}
1711 
1712 		@Override
isColumnSupported(RulerColumnDescriptor descriptor)1713 		public final boolean isColumnSupported(RulerColumnDescriptor descriptor) {
1714 			Assert.isLegal(descriptor != null);
1715 			if (getRuler() == null)
1716 				return false;
1717 
1718 			return descriptor.matchesEditor(fEditor);
1719 		}
1720 
1721 		/**
1722 		 * Returns the editor's vertical ruler, if it is a {@link CompositeRuler}, <code>null</code>
1723 		 * otherwise.
1724 		 *
1725 		 * @return the editor's {@link CompositeRuler} or <code>null</code>
1726 		 */
getRuler()1727 		private CompositeRuler getRuler() {
1728 			Object ruler= fEditor.getAdapter(IVerticalRulerInfo.class);
1729 			if (ruler instanceof CompositeRuler)
1730 				return (CompositeRuler) ruler;
1731 			return null;
1732 		}
1733 
1734 		/**
1735 		 * {@inheritDoc}
1736 		 * <p>
1737 		 * Subclasses may extend this method.</p>
1738 		 *
1739 		 */
1740 		@Override
dispose()1741 		public void dispose() {
1742 			for (Iterator<IContributedRulerColumn> iter= new ArrayList<>(fColumns).iterator(); iter.hasNext();)
1743 				removeColumn(getRuler(), iter.next());
1744 			fColumns.clear();
1745 		}
1746 	}
1747 
1748 
1749 
1750 	/**
1751 	 * This action behaves in two different ways: If there is no current text
1752 	 * hover, the javadoc is displayed using information presenter. If there is
1753 	 * a current text hover, it is converted into a information presenter in
1754 	 * order to make it sticky.
1755 	 *
1756 	 * @since 3.3
1757 	 */
1758 	private final class InformationDispatchAction extends TextEditorAction {
1759 
1760 		/** The wrapped text operation action. */
1761 		private final TextOperationAction fTextOperationAction;
1762 
1763 		/**
1764 		 * Creates a dispatch action.
1765 		 *
1766 		 * @param resourceBundle the resource bundle
1767 		 * @param prefix the prefix
1768 		 * @param textOperationAction the text operation action
1769 		 */
InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction)1770 		public InformationDispatchAction(ResourceBundle resourceBundle, String prefix, final TextOperationAction textOperationAction) {
1771 			super(resourceBundle, prefix, AbstractTextEditor.this);
1772 			if (textOperationAction == null)
1773 				throw new IllegalArgumentException();
1774 			fTextOperationAction= textOperationAction;
1775 		}
1776 
1777 		@Override
run()1778 		public void run() {
1779 
1780 			ISourceViewer sourceViewer= getSourceViewer();
1781 			if (sourceViewer == null) {
1782 				if (fTextOperationAction.isEnabled())
1783 					fTextOperationAction.run();
1784 				return;
1785 			}
1786 
1787 			if (sourceViewer instanceof ITextViewerExtension4)  {
1788 				ITextViewerExtension4 extension4= (ITextViewerExtension4) sourceViewer;
1789 				if (extension4.moveFocusToWidgetToken())
1790 					return;
1791 			}
1792 
1793 			if (sourceViewer instanceof ITextViewerExtension2) {
1794 				// does a text hover exist?
1795 				ITextHover textHover= ((ITextViewerExtension2) sourceViewer).getCurrentTextHover();
1796 				if (textHover != null && makeTextHoverFocusable(sourceViewer, textHover))
1797 					return;
1798 			}
1799 
1800 			if (sourceViewer instanceof ISourceViewerExtension3) {
1801 				// does an annotation hover exist?
1802 				IAnnotationHover annotationHover= ((ISourceViewerExtension3) sourceViewer).getCurrentAnnotationHover();
1803 				if (annotationHover != null && makeAnnotationHoverFocusable(annotationHover))
1804 					return;
1805 			}
1806 
1807 			// otherwise, just run the action
1808 			if (fTextOperationAction.isEnabled())
1809 				fTextOperationAction.run();
1810 		}
1811 
1812 		/**
1813 		 * Tries to make a text hover focusable (or "sticky").
1814 		 *
1815 		 * @param sourceViewer the source viewer to display the hover over
1816 		 * @param textHover the hover to make focusable
1817 		 * @return <code>true</code> if successful, <code>false</code> otherwise
1818 		 */
makeTextHoverFocusable(ISourceViewer sourceViewer, ITextHover textHover)1819 		private boolean makeTextHoverFocusable(ISourceViewer sourceViewer, ITextHover textHover) {
1820 			Point hoverEventLocation= ((ITextViewerExtension2) sourceViewer).getHoverEventLocation();
1821 			int offset= computeOffsetAtLocation(sourceViewer, hoverEventLocation.x, hoverEventLocation.y);
1822 			if (offset == -1)
1823 				return false;
1824 
1825 			try {
1826 				IRegion hoverRegion= textHover.getHoverRegion(sourceViewer, offset);
1827 				if (hoverRegion == null)
1828 					return false;
1829 
1830 				String hoverInfo= textHover.getHoverInfo(sourceViewer, hoverRegion);
1831 
1832 				IInformationControlCreator controlCreator= null;
1833 				if (textHover instanceof IInformationProviderExtension2) // this is conceptually wrong, but left here for backwards compatibility
1834 					controlCreator= ((IInformationProviderExtension2)textHover).getInformationPresenterControlCreator();
1835 
1836 				IInformationProvider informationProvider= new FocusedInformationPresenter.InformationProvider(hoverRegion, hoverInfo, controlCreator);
1837 
1838 				FocusedInformationPresenter informationPresenter= getInformationPresenter();
1839 				informationPresenter.setOffset(offset);
1840 				informationPresenter.setAnchor(AbstractInformationControlManager.ANCHOR_BOTTOM);
1841 				informationPresenter.setMargins(6, 6); // default values from AbstractInformationControlManager
1842 				String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), getSourceViewerConfiguration().getConfiguredDocumentPartitioning(getSourceViewer()), offset, true);
1843 				informationPresenter.setInformationProvider(informationProvider, contentType);
1844 				informationPresenter.showInformation();
1845 
1846 				return true;
1847 
1848 			} catch (BadLocationException e) {
1849 				return false;
1850 			}
1851 		}
1852 
1853 		/**
1854 		 * Tries to make an annotation hover focusable (or "sticky").
1855 		 *
1856 		 * @param annotationHover the hover to make focusable
1857 		 * @return <code>true</code> if successful, <code>false</code> otherwise
1858 		 */
makeAnnotationHoverFocusable(IAnnotationHover annotationHover)1859 		private boolean makeAnnotationHoverFocusable(IAnnotationHover annotationHover) {
1860 			IVerticalRulerInfo info= getVerticalRuler();
1861 			int line= info.getLineOfLastMouseButtonActivity();
1862 			if (line == -1)
1863 				return false;
1864 
1865 			return getInformationPresenter().openFocusedAnnotationHover(annotationHover, line);
1866 		}
1867 
1868 		/**
1869 		 * Returns the information presenter (creates it if necessary).
1870 		 *
1871 		 * @return the information presenter
1872 		 * @since 3.6
1873 		 */
getInformationPresenter()1874 		private FocusedInformationPresenter getInformationPresenter() {
1875 			if (fInformationPresenter == null) {
1876 				fInformationPresenter= new FocusedInformationPresenter(getSourceViewer(), getSourceViewerConfiguration());
1877 			}
1878 			return fInformationPresenter;
1879 		}
1880 
1881 		// modified version from TextViewer
computeOffsetAtLocation(ITextViewer textViewer, int x, int y)1882 		private int computeOffsetAtLocation(ITextViewer textViewer, int x, int y) {
1883 
1884 			StyledText styledText= textViewer.getTextWidget();
1885 			IDocument document= textViewer.getDocument();
1886 
1887 			int widgetOffset = styledText.getOffsetAtPoint(new Point(x, y));
1888 			if (document == null || widgetOffset == -1) {
1889 				return -1;
1890 			}
1891 
1892 			try {
1893 				Point p= styledText.getLocationAtOffset(widgetOffset);
1894 				if (p.x > x) {
1895 					widgetOffset--;
1896 				}
1897 
1898 				if (textViewer instanceof ITextViewerExtension5) {
1899 					ITextViewerExtension5 extension= (ITextViewerExtension5) textViewer;
1900 					return extension.widgetOffset2ModelOffset(widgetOffset);
1901 				}
1902 				IRegion visibleRegion= textViewer.getVisibleRegion();
1903 				return widgetOffset + visibleRegion.getOffset();
1904 			} catch (IllegalArgumentException e) {
1905 				return -1;
1906 			}
1907 		}
1908 	}
1909 
1910 	/**
1911 	 * Key used to look up font preference.
1912 	 * Value: <code>"org.eclipse.jface.textfont"</code>
1913 	 *
1914 	 * @deprecated As of 2.1, replaced by {@link JFaceResources#TEXT_FONT}
1915 	 */
1916 	@Deprecated
1917 	public static final String PREFERENCE_FONT= JFaceResources.TEXT_FONT;
1918 	/**
1919 	 * Key used to look up foreground color preference.
1920 	 * Value: <code>AbstractTextEditor.Color.Foreground</code>
1921 	 * @since 2.0
1922 	 */
1923 	public static final String PREFERENCE_COLOR_FOREGROUND= "AbstractTextEditor.Color.Foreground"; //$NON-NLS-1$
1924 	/**
1925 	 * Key used to look up background color preference.
1926 	 * Value: <code>AbstractTextEditor.Color.Background</code>
1927 	 * @since 2.0
1928 	 */
1929 	public static final String PREFERENCE_COLOR_BACKGROUND= "AbstractTextEditor.Color.Background"; //$NON-NLS-1$
1930 	/**
1931 	 * Key used to look up foreground color system default preference.
1932 	 * Value: <code>AbstractTextEditor.Color.Foreground.SystemDefault</code>
1933 	 * @since 2.0
1934 	 */
1935 	public static final String PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Foreground.SystemDefault"; //$NON-NLS-1$
1936 	/**
1937 	 * Key used to look up background color system default preference.
1938 	 * Value: <code>AbstractTextEditor.Color.Background.SystemDefault</code>
1939 	 * @since 2.0
1940 	 */
1941 	public static final String PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.Background.SystemDefault"; //$NON-NLS-1$
1942 	/**
1943 	 * Key used to look up selection foreground color preference.
1944 	 * Value: <code>AbstractTextEditor.Color.SelectionForeground</code>
1945 	 * @since 3.0
1946 	 */
1947 	public static final String PREFERENCE_COLOR_SELECTION_FOREGROUND= "AbstractTextEditor.Color.SelectionForeground"; //$NON-NLS-1$
1948 	/**
1949 	 * Key used to look up selection background color preference.
1950 	 * Value: <code>AbstractTextEditor.Color.SelectionBackground</code>
1951 	 * @since 3.0
1952 	 */
1953 	public static final String PREFERENCE_COLOR_SELECTION_BACKGROUND= "AbstractTextEditor.Color.SelectionBackground"; //$NON-NLS-1$
1954 	/**
1955 	 * Key used to look up selection foreground color system default preference.
1956 	 * Value: <code>AbstractTextEditor.Color.SelectionForeground.SystemDefault</code>
1957 	 * @since 3.0
1958 	 */
1959 	public static final String PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionForeground.SystemDefault"; //$NON-NLS-1$
1960 	/**
1961 	 * Key used to look up selection background color system default preference.
1962 	 * Value: <code>AbstractTextEditor.Color.SelectionBackground.SystemDefault</code>
1963 	 * @since 3.0
1964 	 */
1965 	public static final String PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT= "AbstractTextEditor.Color.SelectionBackground.SystemDefault"; //$NON-NLS-1$
1966 	/**
1967 	 * Key used to look up find scope background color preference.
1968 	 * Value: <code>AbstractTextEditor.Color.FindScope</code>
1969 	 * @since 2.0
1970 	 */
1971 	public static final String PREFERENCE_COLOR_FIND_SCOPE= "AbstractTextEditor.Color.FindScope"; //$NON-NLS-1$
1972 	/**
1973 	 * Key used to look up smart home/end preference.
1974 	 * Value: <code>AbstractTextEditor.Navigation.SmartHomeEnd</code>
1975 	 * @since 2.1
1976 	 */
1977 	public static final String PREFERENCE_NAVIGATION_SMART_HOME_END= "AbstractTextEditor.Navigation.SmartHomeEnd"; //$NON-NLS-1$
1978 	/**
1979 	 * Key used to look up the custom caret preference.
1980 	 * Value: {@value}
1981 	 * @since 3.0
1982 	 */
1983 	public static final String PREFERENCE_USE_CUSTOM_CARETS= "AbstractTextEditor.Accessibility.UseCustomCarets"; //$NON-NLS-1$
1984 	/**
1985 	 * Key used to look up the caret width preference.
1986 	 * Value: {@value}
1987 	 * @since 3.0
1988 	 */
1989 	public static final String PREFERENCE_WIDE_CARET= "AbstractTextEditor.Accessibility.WideCaret"; //$NON-NLS-1$
1990 	/**
1991 	 * A named preference that controls if hyperlinks are turned on or off.
1992 	 * <p>
1993 	 * Value is of type <code>Boolean</code>.
1994 	 * </p>
1995 	 *
1996 	 * @since 3.1
1997 	 */
1998 	public static final String PREFERENCE_HYPERLINKS_ENABLED= "hyperlinksEnabled"; //$NON-NLS-1$
1999 
2000 	/**
2001 	 * A named preference that controls the key modifier for hyperlinks.
2002 	 * <p>
2003 	 * Value is of type <code>String</code>.
2004 	 * </p>
2005 	 *
2006 	 * @since 3.1
2007 	 */
2008 	public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER= "hyperlinkKeyModifier"; //$NON-NLS-1$
2009 	/**
2010 	 * A named preference that controls the key modifier mask for hyperlinks.
2011 	 * The value is only used if the value of <code>PREFERENCE_HYPERLINK_KEY_MODIFIER</code>
2012 	 * cannot be resolved to valid SWT modifier bits.
2013 	 * <p>
2014 	 * Value is of type <code>String</code>.
2015 	 * </p>
2016 	 *
2017 	 * @see #PREFERENCE_HYPERLINK_KEY_MODIFIER
2018 	 * @since 3.1
2019 	 */
2020 	public static final String PREFERENCE_HYPERLINK_KEY_MODIFIER_MASK= "hyperlinkKeyModifierMask"; //$NON-NLS-1$
2021 	/**
2022 	 * A named preference that controls the visible ruler column contributions.
2023 	 * <p>
2024 	 * Value is of type <code>String</code> and should be read using a {@link RulerColumnPreferenceAdapter}.
2025 	 * </p>
2026 	 *
2027 	 * @since 3.3
2028 	 */
2029 	public static final String PREFERENCE_RULER_CONTRIBUTIONS= "rulerContributions"; //$NON-NLS-1$
2030 
2031 	/**
2032 	 * A named preference that controls the display of whitespace characters.
2033 	 * <p>
2034 	 * Value is of type <code>Boolean</code>.
2035 	 * </p>
2036 	 *
2037 	 * <p>
2038 	 * The following preferences can be used for fine-grained configuration when
2039 	 * enabled.
2040 	 * </p>
2041 	 * <ul>
2042 	 * <li>{@link #PREFERENCE_SHOW_LEADING_SPACES}</li>
2043 	 * <li>{@link #PREFERENCE_SHOW_ENCLOSED_SPACES}</li>
2044 	 * <li>{@link #PREFERENCE_SHOW_TRAILING_SPACES}</li>
2045 	 * <li>{@link #PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES}</li>
2046 	 * <li>{@link #PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES}</li>
2047 	 * <li>{@link #PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES}</li>
2048 	 * <li>{@link #PREFERENCE_SHOW_LEADING_TABS}</li>
2049 	 * <li>{@link #PREFERENCE_SHOW_ENCLOSED_TABS}</li>
2050 	 * <li>{@link #PREFERENCE_SHOW_TRAILING_TABS}</li>
2051 	 * <li>{@link #PREFERENCE_SHOW_CARRIAGE_RETURN}</li>
2052 	 * <li>{@link #PREFERENCE_SHOW_LINE_FEED}</li>
2053 	 * <li>{@link #PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE}</li>
2054 	 * </ul>
2055 	 *
2056 	 * @since 3.3
2057 	 */
2058 	public static final String PREFERENCE_SHOW_WHITESPACE_CHARACTERS= "showWhitespaceCharacters"; //$NON-NLS-1$
2059 
2060 	/**
2061 	 * A named preference that controls the display of leading Space characters. The value is used
2062 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2063 	 * <p>
2064 	 * Value is of type <code>Boolean</code>.
2065 	 * </p>
2066 	 *
2067 	 * @since 3.7
2068 	 */
2069 	public static final String PREFERENCE_SHOW_LEADING_SPACES= "showLeadingSpaces"; //$NON-NLS-1$
2070 
2071 	/**
2072 	 * A named preference that controls the display of enclosed Space characters. The value is used
2073 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2074 	 * <p>
2075 	 * Value is of type <code>Boolean</code>.
2076 	 * </p>
2077 	 *
2078 	 * @since 3.7
2079 	 */
2080 	public static final String PREFERENCE_SHOW_ENCLOSED_SPACES= "showEnclosedSpaces"; //$NON-NLS-1$
2081 
2082 	/**
2083 	 * A named preference that controls the display of trailing Space characters. The value is used
2084 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2085 	 * <p>
2086 	 * Value is of type <code>Boolean</code>.
2087 	 * </p>
2088 	 *
2089 	 * @since 3.7
2090 	 */
2091 	public static final String PREFERENCE_SHOW_TRAILING_SPACES= "showTrailingSpaces"; //$NON-NLS-1$
2092 
2093 	/**
2094 	 * A named preference that controls the display of leading Ideographic Space characters. The
2095 	 * value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
2096 	 * <code>true</code>.
2097 	 * <p>
2098 	 * Value is of type <code>Boolean</code>.
2099 	 * </p>
2100 	 *
2101 	 * @since 3.7
2102 	 */
2103 	public static final String PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES= "showLeadingIdeographicSpaces"; //$NON-NLS-1$
2104 
2105 	/**
2106 	 * A named preference that controls the display of enclosed Ideographic Space characters. The
2107 	 * value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
2108 	 * <code>true</code>.
2109 	 * <p>
2110 	 * Value is of type <code>Boolean</code>.
2111 	 * </p>
2112 	 *
2113 	 * @since 3.7
2114 	 */
2115 	public static final String PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES= "showEnclosedIdeographicSpaces"; //$NON-NLS-1$
2116 
2117 	/**
2118 	 * A named preference that controls the display of trailing Ideographic Space characters. The
2119 	 * value is used only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is
2120 	 * <code>true</code>.
2121 	 * <p>
2122 	 * Value is of type <code>Boolean</code>.
2123 	 * </p>
2124 	 *
2125 	 * @since 3.7
2126 	 */
2127 	public static final String PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES= "showTrailingIdeographicSpaces"; //$NON-NLS-1$
2128 
2129 	/**
2130 	 * A named preference that controls the display of leading Tab characters. The value is used
2131 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2132 	 * <p>
2133 	 * Value is of type <code>Boolean</code>.
2134 	 * </p>
2135 	 *
2136 	 * @since 3.7
2137 	 */
2138 	public static final String PREFERENCE_SHOW_LEADING_TABS= "showLeadingTabs"; //$NON-NLS-1$
2139 
2140 	/**
2141 	 * A named preference that controls the display of enclosed Tab characters. The value is used
2142 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2143 	 * <p>
2144 	 * Value is of type <code>Boolean</code>.
2145 	 * </p>
2146 	 *
2147 	 * @since 3.7
2148 	 */
2149 	public static final String PREFERENCE_SHOW_ENCLOSED_TABS= "showEnclosedTabs"; //$NON-NLS-1$
2150 
2151 	/**
2152 	 * A named preference that controls the display of trailing Tab characters. The value is used
2153 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2154 	 * <p>
2155 	 * Value is of type <code>Boolean</code>.
2156 	 * </p>
2157 	 *
2158 	 * @since 3.7
2159 	 */
2160 	public static final String PREFERENCE_SHOW_TRAILING_TABS= "showTrailingTabs"; //$NON-NLS-1$
2161 
2162 	/**
2163 	 * A named preference that controls the display of Carriage Return characters. The value is used
2164 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2165 	 * <p>
2166 	 * Value is of type <code>Boolean</code>.
2167 	 * </p>
2168 	 *
2169 	 * @since 3.7
2170 	 */
2171 	public static final String PREFERENCE_SHOW_CARRIAGE_RETURN= "showCarriageReturn"; //$NON-NLS-1$
2172 
2173 	/**
2174 	 * A named preference that controls the display of Line Feed characters. The value is used only
2175 	 * if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2176 	 * <p>
2177 	 * Value is of type <code>Boolean</code>.
2178 	 * </p>
2179 	 *
2180 	 * @since 3.7
2181 	 */
2182 	public static final String PREFERENCE_SHOW_LINE_FEED= "showLineFeed"; //$NON-NLS-1$
2183 
2184 	/**
2185 	 * A named preference that controls the alpha value of whitespace characters. The value is used
2186 	 * only if the value of {@link #PREFERENCE_SHOW_WHITESPACE_CHARACTERS} is <code>true</code>.
2187 	 * <p>
2188 	 * Value is of type <code>Integer</code>.
2189 	 * </p>
2190 	 *
2191 	 * @since 3.7
2192 	 */
2193 	public static final String PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE= "whitespaceCharacterAlphaValue"; //$NON-NLS-1$
2194 
2195 	/**
2196 	 * A named preference that controls whether text drag and drop is enabled.
2197 	 * <p>
2198 	 * Value is of type <code>Boolean</code>.
2199 	 * </p>
2200 	 *
2201 	 * @since 3.3
2202 	 */
2203 	public static final String PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED= "textDragAndDropEnabled"; //$NON-NLS-1$
2204 
2205 	/**
2206 	 * A named preference that controls if hovers should automatically be closed
2207 	 * when the mouse is moved into them, or when they should be enriched.
2208 	 * <p>
2209 	 * Value is of type <code>Integer</code> and maps to the following
2210 	 * {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode}:
2211 	 * </p>
2212 	 * <ul>
2213 	 * <li>-1: <code>null</code> (don't allow moving the mouse into a hover),</li>
2214 	 * <li>0: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#AFTER_DELAY},</li>
2215 	 * <li>1: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#IMMEDIATELY},</li>
2216 	 * <li>2: {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode#ON_CLICK}.</li>
2217 	 * </ul>
2218 	 *
2219 	 * @since 3.4
2220 	 */
2221 	public static final String PREFERENCE_HOVER_ENRICH_MODE= "hoverReplaceMode"; //$NON-NLS-1$
2222 
2223 	/**
2224 	 * A named preference to control the initial word wrap status.
2225 	 * <p>
2226 	 * Value is of type <code>Boolean</code>.
2227 	 * </p>
2228 	 *
2229 	 * @since 3.10
2230 	 */
2231 	public static final String PREFERENCE_WORD_WRAP_ENABLED= "wordwrap.enabled"; //$NON-NLS-1$
2232 
2233 	/**
2234 	 * A named preference to control the initial caret offset visibility on the status line.
2235 	 * <p>
2236 	 * Value is of type <code>Boolean</code>.
2237 	 * </p>
2238 	 *
2239 	 * @since 3.13
2240 	 */
2241 	public static final String PREFERENCE_SHOW_CARET_OFFSET = "showCaretOffset"; //$NON-NLS-1$
2242 
2243 	/**
2244 	 * A named preference to control the selection visibility on the status line.
2245 	 * <p>
2246 	 * Value is of type <code>Boolean</code>.
2247 	 * </p>
2248 	 *
2249 	 * @since 3.13
2250 	 */
2251 	public static final String PREFERENCE_SHOW_SELECTION_SIZE = "showSelectionSize"; //$NON-NLS-1$
2252 
2253 	/** Menu id for the editor context menu. */
2254 	public static final String DEFAULT_EDITOR_CONTEXT_MENU_ID= "#EditorContext"; //$NON-NLS-1$
2255 	/** Menu id for the ruler context menu. */
2256 	public static final String DEFAULT_RULER_CONTEXT_MENU_ID= "#RulerContext"; //$NON-NLS-1$
2257 
2258 	/**
2259 	 * Menu id used to contribute to the editor context menu of all textual editors.
2260 	 *
2261 	 * @since 3.5
2262 	 */
2263 	public static final String COMMON_EDITOR_CONTEXT_MENU_ID= "#AbstractTextEditorContext"; //$NON-NLS-1$
2264 
2265 	/**
2266 	 * Menu id used to contribute to the ruler context menu of all textual editors.
2267 	 *
2268 	 * @since 3.5
2269 	 */
2270 	public static final String COMMON_RULER_CONTEXT_MENU_ID= "#AbstractTextEditorRulerContext"; //$NON-NLS-1$
2271 
2272 	/** The width of the vertical ruler. */
2273 	protected static final int VERTICAL_RULER_WIDTH= 12;
2274 
2275 	/**
2276 	 * The complete mapping between action definition IDs used by eclipse and StyledText actions.
2277 	 *
2278 	 * @since 2.0
2279 	 */
2280 	protected static final IdMapEntry[] ACTION_MAP= new IdMapEntry[] {
2281 		// navigation
2282 		new IdMapEntry(ITextEditorActionDefinitionIds.LINE_UP, ST.LINE_UP),
2283 		new IdMapEntry(ITextEditorActionDefinitionIds.LINE_DOWN, ST.LINE_DOWN),
2284 		new IdMapEntry(ITextEditorActionDefinitionIds.LINE_START, ST.LINE_START),
2285 		new IdMapEntry(ITextEditorActionDefinitionIds.LINE_END, ST.LINE_END),
2286 		new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_PREVIOUS, ST.COLUMN_PREVIOUS),
2287 		new IdMapEntry(ITextEditorActionDefinitionIds.COLUMN_NEXT, ST.COLUMN_NEXT),
2288 		new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_UP, ST.PAGE_UP),
2289 		new IdMapEntry(ITextEditorActionDefinitionIds.PAGE_DOWN, ST.PAGE_DOWN),
2290 		new IdMapEntry(ITextEditorActionDefinitionIds.WORD_PREVIOUS, ST.WORD_PREVIOUS),
2291 		new IdMapEntry(ITextEditorActionDefinitionIds.WORD_NEXT, ST.WORD_NEXT),
2292 		new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_START, ST.TEXT_START),
2293 		new IdMapEntry(ITextEditorActionDefinitionIds.TEXT_END, ST.TEXT_END),
2294 		new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_START, ST.WINDOW_START),
2295 		new IdMapEntry(ITextEditorActionDefinitionIds.WINDOW_END, ST.WINDOW_END),
2296 		// selection
2297 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_UP, ST.SELECT_LINE_UP),
2298 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_DOWN, ST.SELECT_LINE_DOWN),
2299 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_START, ST.SELECT_LINE_START),
2300 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_LINE_END, ST.SELECT_LINE_END),
2301 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_PREVIOUS, ST.SELECT_COLUMN_PREVIOUS),
2302 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_COLUMN_NEXT, ST.SELECT_COLUMN_NEXT),
2303 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_UP, ST.SELECT_PAGE_UP),
2304 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_PAGE_DOWN, ST.SELECT_PAGE_DOWN),
2305 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_PREVIOUS, ST.SELECT_WORD_PREVIOUS),
2306 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WORD_NEXT,  ST.SELECT_WORD_NEXT),
2307 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_START, ST.SELECT_TEXT_START),
2308 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_TEXT_END, ST.SELECT_TEXT_END),
2309 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_START, ST.SELECT_WINDOW_START),
2310 		new IdMapEntry(ITextEditorActionDefinitionIds.SELECT_WINDOW_END, ST.SELECT_WINDOW_END),
2311 		// modification
2312 		new IdMapEntry(IWorkbenchCommandConstants.EDIT_CUT, ST.CUT),
2313 		new IdMapEntry(IWorkbenchCommandConstants.EDIT_COPY, ST.COPY),
2314 		new IdMapEntry(IWorkbenchCommandConstants.EDIT_PASTE, ST.PASTE),
2315 		new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS, ST.DELETE_PREVIOUS),
2316 		new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT, ST.DELETE_NEXT),
2317 		new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_PREVIOUS_WORD, ST.DELETE_WORD_PREVIOUS),
2318 		new IdMapEntry(ITextEditorActionDefinitionIds.DELETE_NEXT_WORD, ST.DELETE_WORD_NEXT),
2319 		// miscellaneous
2320 		new IdMapEntry(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, ST.TOGGLE_OVERWRITE)
2321 	};
2322 
2323 	private final String fReadOnlyLabel= EditorMessages.Editor_statusline_state_readonly_label;
2324 	private final String fWritableLabel= EditorMessages.Editor_statusline_state_writable_label;
2325 	private final String fInsertModeLabel= EditorMessages.Editor_statusline_mode_insert_label;
2326 	private final String fOverwriteModeLabel= EditorMessages.Editor_statusline_mode_overwrite_label;
2327 	private final String fSmartInsertModeLabel= EditorMessages.Editor_statusline_mode_smartinsert_label;
2328 
2329 	/** The error message shown in the status line in case of failed information look up. */
2330 	protected final String fErrorLabel= EditorMessages.Editor_statusline_error_label;
2331 
2332 	/**
2333 	 * Data structure for the position label value.
2334 	 */
2335 	private static class PositionLabelValue {
2336 
2337 		public int fValue;
2338 
2339 		@Override
toString()2340 		public String toString() {
2341 			return String.valueOf(fValue);
2342 		}
2343 	}
2344 	/** The pattern used to show the position label in the status line. */
2345 	private final String fPositionLabelPattern= EditorMessages.Editor_statusline_position_pattern;
2346 	/** The position label value of the current line. */
2347 	private final PositionLabelValue fLineLabel= new PositionLabelValue();
2348 	/** The position label value of the current column. */
2349 	private final PositionLabelValue fColumnLabel= new PositionLabelValue();
2350 	/** The position label value of the current offset. */
2351 	private final PositionLabelValue fOffsetLabel = new PositionLabelValue();
2352 	/** The arguments for the position label pattern. */
2353 	private final Object[] fPositionLabelPatternArguments = new Object[] { fLineLabel, fColumnLabel, fOffsetLabel };
2354 	/**
2355 	 * The column support of this editor.
2356 	 * @since 3.3
2357 	 */
2358 	private IColumnSupport fColumnSupport;
2359 
2360 	/** The editor's explicit document provider. */
2361 	private IDocumentProvider fExplicitDocumentProvider;
2362 	/** The editor's preference store. */
2363 	private IPreferenceStore fPreferenceStore;
2364 	/** The editor's range indicator. */
2365 	private Annotation fRangeIndicator;
2366 	/** The editor's source viewer configuration. */
2367 	private SourceViewerConfiguration fConfiguration;
2368 	/** The editor's source viewer. */
2369 	private ISourceViewer fSourceViewer;
2370 	/**
2371 	 * The editor's selection provider.
2372 	 * @since 2.1
2373 	 */
2374 	private SelectionProvider fSelectionProvider= new SelectionProvider();
2375 	/**
2376 	 * The editor's selection listener.
2377 	 * @since 3.0
2378 	 */
2379 	private SelectionListener fSelectionListener;
2380 	/** The editor's font. */
2381 	private Font fFont;	/**
2382 	 * The editor's foreground color.
2383 	 * @since 2.0
2384 	 */
2385 	private Color fForegroundColor;
2386 	/**
2387 	 * The editor's background color.
2388 	 * @since 2.0
2389 	 */
2390 	private Color fBackgroundColor;
2391 	/**
2392 	 * The editor's selection foreground color.
2393 	 * @since 3.0
2394 	 */
2395 	private Color fSelectionForegroundColor;
2396 	/**
2397 	 * The editor's selection background color.
2398 	 * @since 3.0
2399 	 */
2400 	private Color fSelectionBackgroundColor;
2401 	/**
2402 	 * The find scope's highlight color.
2403 	 * @since 2.0
2404 	 */
2405 	private Color fFindScopeHighlightColor;
2406 
2407 	/**
2408 	 * The editor's status line.
2409 	 * @since 2.1
2410 	 */
2411 	private IEditorStatusLine fEditorStatusLine;
2412 	/** The editor's vertical ruler. */
2413 	private IVerticalRuler fVerticalRuler;
2414 	/** The editor's context menu id. */
2415 	private String fEditorContextMenuId;
2416 	/** The ruler's context menu id. */
2417 	private String fRulerContextMenuId;
2418 	/** The editor's help context id. */
2419 	private String fHelpContextId;
2420 	/** The editor's presentation mode. */
2421 	private boolean fShowHighlightRangeOnly;
2422 	/** The actions registered with the editor. */
2423 	private Map<String, IAction> fActions= new HashMap<>(10);
2424 	/** The actions marked as selection dependent. */
2425 	private List<String> fSelectionActions= new ArrayList<>(5);
2426 	/** The actions marked as content dependent. */
2427 	private List<String> fContentActions= new ArrayList<>(5);
2428 	/**
2429 	 * The actions marked as property dependent.
2430 	 * @since 2.0
2431 	 */
2432 	private List<String> fPropertyActions= new ArrayList<>(5);
2433 	/**
2434 	 * The actions marked as state dependent.
2435 	 * @since 2.0
2436 	 */
2437 	private List<String> fStateActions= new ArrayList<>(5);
2438 	/** The editor's action activation codes. */
2439 	private List<ActionActivationCode> fActivationCodes= new ArrayList<>(2);
2440 	/** The verify key listener for activation code triggering. */
2441 	private ActivationCodeTrigger fActivationCodeTrigger= new ActivationCodeTrigger();
2442 	/** Context menu listener. */
2443 	private IMenuListener fMenuListener;
2444 	/** Vertical ruler mouse listener. */
2445 	private MouseListener fMouseListener;
2446 	/** Selection changed listener. */
2447 	private ISelectionChangedListener fSelectionChangedListener;
2448 	/** Title image to be disposed. */
2449 	private Image fTitleImage;
2450 	/** The text context menu to be disposed. */
2451 	private Menu fTextContextMenu;
2452 	/** The ruler context menu to be disposed. */
2453 	private Menu fRulerContextMenu;
2454 	/** The editor's element state listener. */
2455 	private IElementStateListener fElementStateListener= new ElementStateListener();
2456 	/**
2457 	 * The editor's text input listener.
2458 	 * @since 2.1
2459 	 */
2460 	private TextInputListener fTextInputListener= new TextInputListener();
2461 	/** The editor's text listener. */
2462 	private TextListener fTextListener= new TextListener();
2463 	/** The editor's property change listener. */
2464 	private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener();
2465 	/**
2466 	 * The editor's font properties change listener.
2467 	 * @since 2.1
2468 	 */
2469 	private IPropertyChangeListener fFontPropertyChangeListener= new FontPropertyChangeListener();
2470 
2471 	/**
2472 	 * The editor's activation listener.
2473 	 * @since 2.0
2474 	 */
2475 	private ActivationListener fActivationListener;
2476 	/**
2477 	 * Indicates activation should be handled.
2478 	 * @since 3.9
2479 	 */
2480 	private boolean fHandleActivation= true;
2481 	/**
2482 	 * The map of the editor's status fields.
2483 	 * @since 2.0
2484 	 */
2485 	private Map<String, IStatusField> fStatusFields;
2486 	/**
2487 	 * The editor's cursor listener.
2488 	 * @since 2.0
2489 	 */
2490 	private ICursorListener fCursorListener;
2491 	/**
2492 	 * The editor's remembered text selection.
2493 	 * @since 2.0
2494 	 */
2495 	private ISelection fRememberedSelection;
2496 	/**
2497 	 * Indicates whether the editor runs in 1.0 context menu registration compatibility mode.
2498 	 * @since 2.0
2499 	 */
2500 	private boolean fCompatibilityMode= true;
2501 	/**
2502 	 * The number of re-entrances into error correction code while saving.
2503 	 * @since 2.0
2504 	 */
2505 	private int fErrorCorrectionOnSave;
2506 	/**
2507 	 * The delete line target.
2508 	 * @since 2.1
2509 	 */
2510 	private IDeleteLineTarget fDeleteLineTarget;
2511 	/**
2512 	 * The incremental find target.
2513 	 * @since 2.0
2514 	 */
2515 	private IncrementalFindTarget fIncrementalFindTarget;
2516 	/**
2517 	 * The mark region target.
2518 	 * @since 2.0
2519 	 */
2520 	private IMarkRegionTarget fMarkRegionTarget;
2521 	/**
2522 	 * Cached modification stamp of the editor's input.
2523 	 * @since 2.0
2524 	 */
2525 	private long fModificationStamp= -1;
2526 	/**
2527 	 * Ruler context menu listeners.
2528 	 * @since 2.0
2529 	 */
2530 	private List<IMenuListener> fRulerContextMenuListeners= new ArrayList<>();
2531 	/**
2532 	 * Indicates whether sanity checking in enabled.
2533 	 * @since 2.0
2534 	 */
2535 	private boolean fIsSanityCheckEnabled= true;
2536 	/**
2537 	 * The find replace target.
2538 	 * @since 2.1
2539 	 */
2540 	private FindReplaceTarget fFindReplaceTarget;
2541 	/**
2542 	 * Indicates whether state validation is enabled.
2543 	 * @since 2.1
2544 	 */
2545 	private boolean fIsStateValidationEnabled= true;
2546 	/**
2547 	 * The key binding scopes of this editor.
2548 	 * @since 2.1
2549 	 */
2550 	private String[] fKeyBindingScopes;
2551 	/**
2552 	 * Whether the overwrite mode can be turned on.
2553 	 * @since 3.0
2554 	 */
2555 	private boolean fIsOverwriteModeEnabled= true;
2556 	/**
2557 	 * Whether the overwrite mode is currently on.
2558 	 * @since 3.0
2559 	 */
2560 	private boolean fIsOverwriting= false;
2561 	/**
2562 	 * The editor's insert mode.
2563 	 * @since 3.0
2564 	 */
2565 	private InsertMode fInsertMode= SMART_INSERT;
2566 	/**
2567 	 * The sequence of legal editor insert modes.
2568 	 * @since 3.0
2569 	 */
2570 	private List<InsertMode> fLegalInsertModes= null;
2571 	/**
2572 	 * The non-default caret.
2573 	 * @since 3.0
2574 	 */
2575 	private Caret fNonDefaultCaret;
2576 	/**
2577 	 * The image used in non-default caret.
2578 	 * @since 3.0
2579 	 */
2580 	private Image fNonDefaultCaretImage;
2581 	/**
2582 	 * The styled text's initial caret.
2583 	 * @since 3.0
2584 	 */
2585 	private Caret fInitialCaret;
2586 	/**
2587 	 * The operation approver used to warn on undoing of non-local operations.
2588 	 * @since 3.1
2589 	 */
2590 	private IOperationApprover fNonLocalOperationApprover;
2591 	/**
2592 	 * The operation approver used to warn of linear undo violations.
2593 	 * @since 3.1
2594 	 */
2595 	private IOperationApprover fLinearUndoViolationApprover;
2596 	/**
2597 	 * This editor's memento holding data for restoring it after restart.
2598 	 * @since 3.3
2599 	 */
2600 	private IMemento fMementoToRestore;
2601 	/**
2602 	 * This editor's savable.
2603 	 * @since 3.3
2604 	 */
2605 	private TextEditorSavable fSavable;
2606 	/**
2607 	 * Tells whether text drag and drop has been installed on the control.
2608 	 * @since 3.3
2609 	 */
2610 	private boolean fIsTextDragAndDropInstalled= false;
2611 	/**
2612 	 * Helper token to decide whether drag and
2613 	 * drop happens inside the same editor.
2614 	 * @since 3.3
2615 	 */
2616 	private Object fTextDragAndDropToken;
2617 	/**
2618 	 * The information presenter, may be <code>null</code>.
2619 	 * @since 3.3
2620 	 */
2621 	private FocusedInformationPresenter fInformationPresenter;
2622 
2623 	/**
2624 	 * Tells whether this editor has been activated at least once.
2625 	 * @since 3.3.2
2626 	 */
2627 	private boolean fHasBeenActivated= false;
2628 	/**
2629 	 * Key binding support for the quick assist assistant.
2630 	 * @since 3.4
2631 	 */
2632 	private KeyBindingSupportForAssistant fKeyBindingSupportForQuickAssistant;
2633 
2634 	/**
2635 	 * Key binding support for the quick assist assistant.
2636 	 * @since 3.5
2637 	 */
2638 	private KeyBindingSupportForAssistant fKeyBindingSupportForContentAssistant;
2639 
2640 	/**
2641 	 * The save action.
2642 	 * @since 3.6.1
2643 	 */
2644 	private IWorkbenchAction fSaveAction;
2645 
2646 
2647 	/**
2648 	 * Creates a new text editor. If not explicitly set, this editor uses
2649 	 * a <code>SourceViewerConfiguration</code> to configure its
2650 	 * source viewer. This viewer does not have a range indicator installed,
2651 	 * nor any menu id set. By default, the created editor runs in 1.0 context
2652 	 * menu registration compatibility mode.
2653 	 */
AbstractTextEditor()2654 	protected AbstractTextEditor() {
2655 		super();
2656 		fEditorContextMenuId= null;
2657 		fRulerContextMenuId= null;
2658 		fHelpContextId= null;
2659 	}
2660 
2661 	@Override
getDocumentProvider()2662 	public IDocumentProvider getDocumentProvider() {
2663 		return fExplicitDocumentProvider;
2664 	}
2665 
2666 	/**
2667 	 * Returns the editor's range indicator. May return <code>null</code> if no
2668 	 * range indicator is installed.
2669 	 *
2670 	 * @return the editor's range indicator which may be <code>null</code>
2671 	 */
getRangeIndicator()2672 	protected final Annotation getRangeIndicator() {
2673 		return fRangeIndicator;
2674 	}
2675 
2676 	/**
2677 	 * Returns the editor's source viewer configuration. May return <code>null</code>
2678 	 * before the editor's part has been created and after disposal.
2679 	 *
2680 	 * @return the editor's source viewer configuration which may be <code>null</code>
2681 	 */
getSourceViewerConfiguration()2682 	protected final SourceViewerConfiguration getSourceViewerConfiguration() {
2683 		return fConfiguration;
2684 	}
2685 
2686 	/**
2687 	 * Returns the editor's source viewer. May return <code>null</code> before
2688 	 * the editor's part has been created and after disposal.
2689 	 *
2690 	 * @return the editor's source viewer which may be <code>null</code>
2691 	 */
getSourceViewer()2692 	protected final ISourceViewer getSourceViewer() {
2693 		return fSourceViewer;
2694 	}
2695 
2696 	/**
2697 	 * Returns the editor's vertical ruler. May return <code>null</code> before
2698 	 * the editor's part has been created and after disposal.
2699 	 *
2700 	 * @return the editor's vertical ruler which may be <code>null</code>
2701 	 */
getVerticalRuler()2702 	protected final IVerticalRuler getVerticalRuler() {
2703 		return fVerticalRuler;
2704 	}
2705 
2706 	/**
2707 	 * Returns the editor's context menu id. May return <code>null</code> before
2708 	 * the editor's part has been created.
2709 	 *
2710 	 * @return the editor's context menu id which may be <code>null</code>
2711 	 */
getEditorContextMenuId()2712 	protected final String getEditorContextMenuId() {
2713 		return fEditorContextMenuId;
2714 	}
2715 
2716 	/**
2717 	 * Returns the ruler's context menu id. May return <code>null</code> before
2718 	 * the editor's part has been created.
2719 	 *
2720 	 * @return the ruler's context menu id which may be <code>null</code>
2721 	 */
getRulerContextMenuId()2722 	protected final String getRulerContextMenuId() {
2723 		return fRulerContextMenuId;
2724 	}
2725 
2726 	/**
2727 	 * Returns the editor's help context id or <code>null</code> if none has
2728 	 * been set.
2729 	 *
2730 	 * @return the editor's help context id which may be <code>null</code>
2731 	 */
getHelpContextId()2732 	protected final String getHelpContextId() {
2733 		return fHelpContextId;
2734 	}
2735 
2736 	/**
2737 	 * Returns this editor's preference store or <code>null</code> if none has
2738 	 * been set.
2739 	 *
2740 	 * @return this editor's preference store which may be <code>null</code>
2741 	 */
getPreferenceStore()2742 	protected final IPreferenceStore getPreferenceStore() {
2743 		return fPreferenceStore;
2744 	}
2745 
2746 	/**
2747 	 * Sets this editor's document provider. This method must be
2748 	 * called before the editor's control is created.
2749 	 *
2750 	 * @param provider the document provider
2751 	 */
setDocumentProvider(IDocumentProvider provider)2752 	protected void setDocumentProvider(IDocumentProvider provider) {
2753 		fExplicitDocumentProvider= provider;
2754 	}
2755 
2756 	/**
2757 	 * Sets this editor's source viewer configuration used to configure its
2758 	 * internal source viewer. This method must be called before the editor's
2759 	 * control is created. If not, this editor uses a <code>SourceViewerConfiguration</code>.
2760 	 *
2761 	 * @param configuration the source viewer configuration object
2762 	 */
setSourceViewerConfiguration(SourceViewerConfiguration configuration)2763 	protected void setSourceViewerConfiguration(SourceViewerConfiguration configuration) {
2764 		Assert.isNotNull(configuration);
2765 		fConfiguration= configuration;
2766 	}
2767 
2768 	/**
2769 	 * Sets the annotation which this editor uses to represent the highlight
2770 	 * range if the editor is configured to show the entire document. If the
2771 	 * range indicator is not set, this editor will not show a range indication.
2772 	 *
2773 	 * @param rangeIndicator the annotation
2774 	 */
setRangeIndicator(Annotation rangeIndicator)2775 	protected void setRangeIndicator(Annotation rangeIndicator) {
2776 		Assert.isNotNull(rangeIndicator);
2777 		fRangeIndicator= rangeIndicator;
2778 	}
2779 
2780 	/**
2781 	 * Sets this editor's context menu id.
2782 	 *
2783 	 * @param contextMenuId the context menu id
2784 	 */
setEditorContextMenuId(String contextMenuId)2785 	protected void setEditorContextMenuId(String contextMenuId) {
2786 		Assert.isNotNull(contextMenuId);
2787 		fEditorContextMenuId= contextMenuId;
2788 	}
2789 
2790 	/**
2791 	 * Sets the ruler's context menu id.
2792 	 *
2793 	 * @param contextMenuId the context menu id
2794 	 */
setRulerContextMenuId(String contextMenuId)2795 	protected void setRulerContextMenuId(String contextMenuId) {
2796 		Assert.isNotNull(contextMenuId);
2797 		fRulerContextMenuId= contextMenuId;
2798 	}
2799 
2800 	/**
2801 	 * Sets the context menu registration 1.0 compatibility mode. (See class
2802 	 * description for more details.)
2803 	 *
2804 	 * @param compatible <code>true</code> if compatibility mode is enabled
2805 	 * @since 2.0
2806 	 */
setCompatibilityMode(boolean compatible)2807 	protected final void setCompatibilityMode(boolean compatible) {
2808 		fCompatibilityMode= compatible;
2809 	}
2810 
2811 	/**
2812 	 * Sets the editor's help context id.
2813 	 *
2814 	 * @param helpContextId the help context id
2815 	 */
setHelpContextId(String helpContextId)2816 	protected void setHelpContextId(String helpContextId) {
2817 		Assert.isNotNull(helpContextId);
2818 		fHelpContextId= helpContextId;
2819 	}
2820 
2821 	/**
2822 	 * Sets the key binding scopes for this editor.
2823 	 *
2824 	 * @param scopes a non-empty array of key binding scope identifiers
2825 	 * @since 2.1
2826 	 */
setKeyBindingScopes(String[] scopes)2827 	protected void setKeyBindingScopes(String[] scopes) {
2828 		Assert.isTrue(scopes != null && scopes.length > 0);
2829 		fKeyBindingScopes= scopes;
2830 	}
2831 
2832 	/**
2833 	 * Sets this editor's preference store. This method must be
2834 	 * called before the editor's control is created.
2835 	 *
2836 	 * @param store the preference store or <code>null</code> to remove the
2837 	 * 		  preference store
2838 	 */
setPreferenceStore(IPreferenceStore store)2839 	protected void setPreferenceStore(IPreferenceStore store) {
2840 		if (fPreferenceStore != null) {
2841 			fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
2842 			fPreferenceStore.removePropertyChangeListener(fFontPropertyChangeListener);
2843 		}
2844 
2845 		fPreferenceStore= store;
2846 
2847 		if (fPreferenceStore != null) {
2848 			fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener);
2849 			fPreferenceStore.addPropertyChangeListener(fFontPropertyChangeListener);
2850 		}
2851 	}
2852 
2853 	@Override
isEditable()2854 	public boolean isEditable() {
2855 		IDocumentProvider provider= getDocumentProvider();
2856 		if (provider instanceof IDocumentProviderExtension) {
2857 			IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
2858 			return extension.isModifiable(getEditorInput());
2859 		}
2860 		return false;
2861 	}
2862 
2863 	/**
2864 	 * {@inheritDoc}
2865 	 * <p>
2866 	 * Returns <code>null</code> after disposal.
2867 	 * </p>
2868 	 *
2869 	 * @return the selection provider or <code>null</code> if the editor has
2870 	 *         been disposed
2871 	 */
2872 	@Override
getSelectionProvider()2873 	public ISelectionProvider getSelectionProvider() {
2874 		return fSelectionProvider;
2875 	}
2876 
2877 	/**
2878 	 * Remembers the current selection of this editor. This method is called when, e.g.,
2879 	 * the content of the editor is about to be reverted to the saved state. This method
2880 	 * remembers the selection in a semantic format, i.e., in a format which allows to
2881 	 * restore the selection even if the originally selected text is no longer part of the
2882 	 * editor's content.
2883 	 * <p>
2884 	 * Subclasses should implement this method including all necessary state. This
2885 	 * default implementation remembers the textual range only and is thus purely
2886 	 * syntactic.</p>
2887 	 *
2888 	 * @see #restoreSelection()
2889 	 * @since 2.0
2890 	 */
rememberSelection()2891 	protected void rememberSelection() {
2892 		fRememberedSelection= doGetSelection();
2893 	}
2894 
2895 	/**
2896 	 * Returns the current selection.
2897 	 * @return ISelection
2898 	 * @since 2.1
2899 	 */
doGetSelection()2900 	protected ISelection doGetSelection() {
2901 		ISelectionProvider sp= null;
2902 		if (fSourceViewer != null)
2903 			sp= fSourceViewer.getSelectionProvider();
2904 		return (sp == null ? null : sp.getSelection());
2905 	}
2906 
2907 	/**
2908 	 * Restores a selection previously remembered by <code>rememberSelection</code>.
2909 	 * Subclasses may reimplement this method and thereby semantically adapt the
2910 	 * remembered selection. This default implementation just selects the
2911 	 * remembered textual range.
2912 	 *
2913 	 * @see #rememberSelection()
2914 	 * @since 2.0
2915 	 */
restoreSelection()2916 	protected void restoreSelection() {
2917 		if (fRememberedSelection instanceof ITextSelection) {
2918 			ITextSelection textSelection= (ITextSelection)fRememberedSelection;
2919 			if (isValidSelection(textSelection.getOffset(), textSelection.getLength()))
2920 				doSetSelection(fRememberedSelection);
2921 		}
2922 		fRememberedSelection= null;
2923 	}
2924 
2925 	/**
2926 	 * Tells whether the given selection is valid.
2927 	 *
2928 	 * @param offset the offset of the selection
2929 	 * @param length the length of the selection
2930 	 * @return <code>true</code> if the selection is valid
2931 	 * @since 2.1
2932 	 */
isValidSelection(int offset, int length)2933 	private boolean isValidSelection(int offset, int length) {
2934 		IDocumentProvider provider= getDocumentProvider();
2935 		if (provider != null) {
2936 			IDocument document= provider.getDocument(getEditorInput());
2937 			if (document != null) {
2938 				int end= offset + length;
2939 				int documentLength= document.getLength();
2940 				return 0 <= offset  && offset <= documentLength && 0 <= end && end <= documentLength && length >= 0;
2941 			}
2942 		}
2943 		return false;
2944 	}
2945 
2946 	/**
2947 	 * Sets the given selection.
2948 	 *
2949 	 * @param selection the selection
2950 	 * @since 2.1
2951 	 */
doSetSelection(ISelection selection)2952 	protected void doSetSelection(ISelection selection) {
2953 		if (selection instanceof ITextSelection) {
2954 			ITextSelection textSelection= (ITextSelection) selection;
2955 			selectAndReveal(textSelection.getOffset(), textSelection.getLength());
2956 		}
2957 	}
2958 
2959 	/**
2960 	 * Creates the listener on this editor's context menus.
2961 	 *
2962 	 * @return the created menu listener
2963 	 * @since 3.4
2964 	 */
createContextMenuListener()2965 	protected IMenuListener createContextMenuListener() {
2966 		return menu -> {
2967 			String id = menu.getId();
2968 			if (getRulerContextMenuId().equals(id)) {
2969 				setFocus();
2970 				rulerContextMenuAboutToShow(menu);
2971 			} else if (getEditorContextMenuId().equals(id)) {
2972 				setFocus();
2973 				editorContextMenuAboutToShow(menu);
2974 			}
2975 		};
2976 	}
2977 
2978 	/**
2979 	 * Creates and returns the listener on this editor's context menus.
2980 	 *
2981 	 * @return the menu listener
2982 	 */
getContextMenuListener()2983 	protected final IMenuListener getContextMenuListener() {
2984 		if (fMenuListener == null)
2985 			fMenuListener= createContextMenuListener();
2986 		return fMenuListener;
2987 	}
2988 
2989 	/**
2990 	 * Creates and returns the listener on this editor's vertical ruler.
2991 	 *
2992 	 * @return the mouse listener
2993 	 */
getRulerMouseListener()2994 	protected final MouseListener getRulerMouseListener() {
2995 		if (fMouseListener == null) {
2996 			fMouseListener= new MouseListener() {
2997 
2998 				private boolean fDoubleClicked= false;
2999 				private final int fDoubleClickTime= getSite().getShell().getDisplay().getDoubleClickTime();
3000 				private long fMouseUpDelta= 0;
3001 
3002 				private void triggerAction(String actionID, MouseEvent e) {
3003 					// ActionId can be prefixed with modifiers
3004 					StringBuilder newActionId = new StringBuilder(""); //$NON-NLS-1$
3005 					if ((e.stateMask & SWT.MOD1) > 0) {
3006 						newActionId.append("M1+"); //$NON-NLS-1$
3007 					}
3008 					if ((e.stateMask & SWT.MOD2) > 0) {
3009 						newActionId.append("M2+"); //$NON-NLS-1$
3010 					}
3011 					if ((e.stateMask & SWT.MOD3) > 0) {
3012 						newActionId.append("M3+"); //$NON-NLS-1$
3013 					}
3014 					newActionId.append(actionID);
3015 					IAction action = getAction(newActionId.toString());
3016 					// If action does not exist with specified
3017 					// modifiers+actionId, try to retrieve action with only
3018 					// actionId
3019 					if (action == null) {
3020 						action = getAction(actionID);
3021 					}
3022 					if (action != null) {
3023 						if (action instanceof IUpdate)
3024 							((IUpdate) action).update();
3025 						if (action.isEnabled()) {
3026 							Event event= new Event();
3027 							event.type= fDoubleClicked ? SWT.MouseDoubleClick : SWT.MouseUp;
3028 							event.display= e.display;
3029 							event.widget= e.widget;
3030 							event.time= e.time;
3031 							event.data= e.data;
3032 							event.x= e.x;
3033 							event.y= e.y;
3034 							event.button= e.button;
3035 							event.stateMask= e.stateMask;
3036 							event.count= e.count;
3037 							action.runWithEvent(event);
3038 						}
3039 					}
3040 				}
3041 
3042 				@Override
3043 				public void mouseUp(final MouseEvent e) {
3044 					setFocus();
3045 					final int delay= fMouseUpDelta == 0 ? 0 : fDoubleClickTime - (int)(System.currentTimeMillis() - fMouseUpDelta);
3046 					if (1 != e.button)
3047 						return;
3048 
3049 					Runnable runnable = () -> {
3050 						if (!fDoubleClicked)
3051 							triggerAction(ITextEditorActionConstants.RULER_CLICK, e);
3052 					};
3053 					if (delay <= 0)
3054 						runnable.run();
3055 					else
3056 						e.widget.getDisplay().timerExec(delay, runnable);
3057 				}
3058 
3059 				@Override
3060 				public void mouseDoubleClick(MouseEvent e) {
3061 					if (1 == e.button) {
3062 						fDoubleClicked= true;
3063 						triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK, e);
3064 					}
3065 				}
3066 
3067 				@Override
3068 				public void mouseDown(MouseEvent e) {
3069 					fMouseUpDelta= System.currentTimeMillis();
3070 					fDoubleClicked= false;
3071 					if (fRulerContextMenu != null && !fRulerContextMenu.isDisposed()) {
3072 						Display display= fRulerContextMenu.getDisplay();
3073 						Point location= display.getCursorLocation();
3074 						fRulerContextMenu.setLocation(location.x, location.y);
3075 					}
3076 				}
3077 			};
3078 		}
3079 		return fMouseListener;
3080 	}
3081 
3082 	/**
3083 	 * Returns this editor's selection changed listener to be installed
3084 	 * on the editor's source viewer.
3085 	 *
3086 	 * @return the listener
3087 	 */
getSelectionChangedListener()3088 	protected final ISelectionChangedListener getSelectionChangedListener() {
3089 		if (fSelectionChangedListener == null) {
3090 			fSelectionChangedListener= new ISelectionChangedListener() {
3091 
3092 				private Runnable fRunnable = () -> {
3093 					// check whether editor has not been disposed yet
3094 					if (fSourceViewer != null && fSourceViewer.getDocument() != null) {
3095 						handleCursorPositionChanged();
3096 						updateSelectionDependentActions();
3097 					}
3098 				};
3099 
3100 				private Display fDisplay;
3101 
3102 				@Override
3103 				public void selectionChanged(SelectionChangedEvent event) {
3104 					if (fDisplay == null)
3105 						fDisplay= getSite().getShell().getDisplay();
3106 					if (Display.getCurrent() == fDisplay)
3107 						fRunnable.run();
3108 					else
3109 						fDisplay.asyncExec(fRunnable);
3110 				}
3111 			};
3112 		}
3113 
3114 		return fSelectionChangedListener;
3115 	}
3116 
3117 	/**
3118 	 * Returns this editor's "cursor" listener to be installed on the editor's
3119 	 * source viewer. This listener is listening to key and mouse button events.
3120 	 * It triggers the updating of the status line by calling
3121 	 * <code>handleCursorPositionChanged()</code>.
3122 	 *
3123 	 * @return the listener
3124 	 * @since 2.0
3125 	 */
getCursorListener()3126 	protected final ICursorListener getCursorListener() {
3127 		if (fCursorListener == null) {
3128 			fCursorListener= new ICursorListener() {
3129 
3130 				@Override
3131 				public void keyPressed(KeyEvent e) {
3132 					handleCursorPositionChanged();
3133 				}
3134 
3135 				@Override
3136 				public void keyReleased(KeyEvent e) {
3137 				}
3138 
3139 				@Override
3140 				public void mouseDoubleClick(MouseEvent e) {
3141 				}
3142 
3143 				@Override
3144 				public void mouseDown(MouseEvent e) {
3145 				}
3146 
3147 				@Override
3148 				public void mouseUp(MouseEvent e) {
3149 					handleCursorPositionChanged();
3150 				}
3151 			};
3152 		}
3153 		return fCursorListener;
3154 	}
3155 
3156 	/**
3157 	 * Implements the <code>init</code> method of <code>IEditorPart</code>.
3158 	 * Subclasses replacing <code>init</code> may choose to call this method in
3159 	 * their implementation.
3160 	 *
3161 	 * @param window the workbench window
3162 	 * @param site the editor's site
3163 	 * @param input the editor input for the editor being created
3164 	 * @throws PartInitException if {@link #doSetInput(IEditorInput)} fails or gets canceled
3165 	 *
3166 	 * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
3167 	 * @since 2.1
3168 	 */
internalInit(IWorkbenchWindow window, final IEditorSite site, final IEditorInput input)3169 	protected final void internalInit(IWorkbenchWindow window, final IEditorSite site, final IEditorInput input) throws PartInitException {
3170 
3171 		IRunnableWithProgress runnable = monitor -> {
3172 			try {
3173 
3174 				if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
3175 					IDocumentProviderExtension2 extension1 = (IDocumentProviderExtension2) getDocumentProvider();
3176 					extension1.setProgressMonitor(monitor);
3177 				}
3178 
3179 				doSetInput(input);
3180 
3181 			} catch (CoreException x) {
3182 				throw new InvocationTargetException(x);
3183 			} finally {
3184 				if (getDocumentProvider() instanceof IDocumentProviderExtension2) {
3185 					IDocumentProviderExtension2 extension2 = (IDocumentProviderExtension2) getDocumentProvider();
3186 					extension2.setProgressMonitor(null);
3187 				}
3188 			}
3189 		};
3190 
3191 		try {
3192 //			When using the progress service always a modal dialog pops up. The site should be asked for a runnable context
3193 //			which could be the workbench window or the progress service, depending on what the site represents.
3194 //			getSite().getWorkbenchWindow().getWorkbench().getProgressService().run(false, true, runnable);
3195 
3196 			getSite().getWorkbenchWindow().run(false, true, runnable);
3197 
3198 		} catch (InterruptedException x) {
3199 		} catch (InvocationTargetException x) {
3200 			Throwable t= x.getTargetException();
3201 			if (t instanceof CoreException) {
3202 				/*
3203 				/* XXX: Remove unpacking of CoreException once the following bug is
3204 				 *		fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640
3205 				 */
3206 				CoreException e= (CoreException)t;
3207 				IStatus status= e.getStatus();
3208 				if (status.getException() != null)
3209 					throw new PartInitException(status);
3210 				throw new PartInitException(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), t));
3211 			}
3212 			throw new PartInitException(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_init, t));
3213 		}
3214 	}
3215 
3216 	@Override
init(final IEditorSite site, final IEditorInput input)3217 	public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {
3218 
3219 		setSite(site);
3220 
3221 		internalInit(site.getWorkbenchWindow(), site, input);
3222 		fActivationListener= new ActivationListener(site.getWorkbenchWindow().getPartService());
3223 	}
3224 
3225 	/**
3226 	 * Creates the vertical ruler to be used by this editor.
3227 	 * Subclasses may re-implement this method.
3228 	 *
3229 	 * @return the vertical ruler
3230 	 */
createVerticalRuler()3231 	protected IVerticalRuler createVerticalRuler() {
3232 		return new VerticalRuler(VERTICAL_RULER_WIDTH);
3233 	}
3234 
3235 	/**
3236 	 * Adds enabled ruler contributions to the vertical ruler.
3237 	 * <p>
3238 	 * Clients may extend or replace.</p>
3239 	 *
3240 	 * @param ruler the composite ruler to add contributions to
3241 	 * @since 3.3
3242 	 */
updateContributedRulerColumns(CompositeRuler ruler)3243 	protected void updateContributedRulerColumns(CompositeRuler ruler) {
3244 		IColumnSupport support= getAdapter(IColumnSupport.class);
3245 		if (support == null)
3246 			return;
3247 
3248 		RulerColumnPreferenceAdapter adapter= null;
3249 		if (fPreferenceStore != null)
3250 			adapter= new RulerColumnPreferenceAdapter(getPreferenceStore(), PREFERENCE_RULER_CONTRIBUTIONS);
3251 
3252 		RulerColumnRegistry registry= RulerColumnRegistry.getDefault();
3253 		List<RulerColumnDescriptor> descriptors= registry.getColumnDescriptors();
3254 		for (Iterator<RulerColumnDescriptor> it= descriptors.iterator(); it.hasNext();) {
3255 			final RulerColumnDescriptor descriptor= it.next();
3256 			support.setColumnVisible(descriptor, adapter == null || adapter.isEnabled(descriptor));
3257 		}
3258 	}
3259 
3260 	/**
3261 	 * Creates the column support to be used by this editor to manage the
3262 	 * contributed ruler columns.
3263 	 * Subclasses may re-implement this method using the {@link ColumnSupport},
3264 	 * e.g. by returning <code>new ColumnSupport(this, RulerColumnRegistry.getDefault());</code>.
3265 	 * <p>
3266 	 * <strong>Note:</strong> If you override this method to provide column support you will
3267 	 * also need to override {@link #createVerticalRuler()} to return a {@link CompositeRuler}.</p>
3268 	 * <p>
3269 	 * Out of the box this class does not install this support and hence this
3270 	 * implementation always returns <code>null</code>.</p>
3271 	 *
3272 	 * @return the column support or <code>null</code> if none
3273 	 * @since 3.3
3274 	 */
createColumnSupport()3275 	protected IColumnSupport createColumnSupport() {
3276 		return null;
3277 	}
3278 
3279 	/**
3280 	 * Creates the source viewer to be used by this editor.
3281 	 * Subclasses may re-implement this method.
3282 	 *
3283 	 * @param parent the parent control
3284 	 * @param ruler the vertical ruler
3285 	 * @param styles style bits, <code>SWT.WRAP</code> is currently not supported
3286 	 * @return the source viewer
3287 	 */
createSourceViewer(Composite parent, IVerticalRuler ruler, int styles)3288 	protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {
3289 		return new SourceViewer(parent, ruler, styles);
3290 	}
3291 
3292 	/**
3293 	 * Initializes the drag and drop support for the given viewer based on
3294 	 * provided editor adapter for drop target listeners.
3295 	 *
3296 	 * @param viewer the viewer
3297 	 * @since 3.0
3298 	 */
initializeDragAndDrop(ISourceViewer viewer)3299 	protected void initializeDragAndDrop(ISourceViewer viewer) {
3300 		IDragAndDropService dndService= getSite().getService(IDragAndDropService.class);
3301 		if (dndService == null)
3302 			return;
3303 
3304 		ITextEditorDropTargetListener listener= getAdapter(ITextEditorDropTargetListener.class);
3305 
3306 		if (listener == null) {
3307 			Object object= Platform.getAdapterManager().loadAdapter(this, "org.eclipse.ui.texteditor.ITextEditorDropTargetListener"); //$NON-NLS-1$
3308 			if (object instanceof ITextEditorDropTargetListener)
3309 				listener= (ITextEditorDropTargetListener)object;
3310 		}
3311 
3312 		if (listener != null)
3313 			dndService.addMergedDropTarget(viewer.getTextWidget(), DND.DROP_MOVE | DND.DROP_COPY, listener.getTransfers(), listener);
3314 
3315 		IPreferenceStore store= getPreferenceStore();
3316 		if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
3317 			installTextDragAndDrop(viewer);
3318 
3319 	}
3320 
3321 	/**
3322 	 * The <code>AbstractTextEditor</code> implementation of this
3323 	 * <code>IWorkbenchPart</code> method creates the vertical ruler and source
3324 	 * viewer.
3325 	 * <p>
3326 	 * Subclasses may extend this method. Besides extending this method, the
3327 	 * behavior of <code>createPartControl</code> may be customized by calling,
3328 	 * extending or replacing the following methods: <br>
3329 	 * Subclasses may supply customized implementations for some members using
3330 	 * the following methods before <code>createPartControl</code> is invoked:
3331 	 * </p>
3332 	 * <ul>
3333 	 * <li>{@linkplain #setSourceViewerConfiguration(SourceViewerConfiguration)
3334 	 * setSourceViewerConfiguration} to supply a custom source viewer
3335 	 * configuration,</li>
3336 	 * <li>{@linkplain #setRangeIndicator(Annotation) setRangeIndicator} to
3337 	 * provide a range indicator,</li>
3338 	 * <li>{@linkplain #setHelpContextId(String) setHelpContextId} to provide a
3339 	 * help context id,</li>
3340 	 * <li>{@linkplain #setEditorContextMenuId(String) setEditorContextMenuId}
3341 	 * to set a custom context menu id,</li>
3342 	 * <li>{@linkplain #setRulerContextMenuId(String) setRulerContextMenuId} to
3343 	 * set a custom ruler context menu id.</li>
3344 	 * </ul>
3345 	 * <br>
3346 	 * Subclasses may replace the following methods called from within
3347 	 * <code>createPartControl</code>:
3348 	 * <ul>
3349 	 * <li>{@linkplain #createVerticalRuler() createVerticalRuler} to supply a
3350 	 * custom vertical ruler,</li>
3351 	 * <li>{@linkplain #createSourceViewer(Composite, IVerticalRuler, int)
3352 	 * createSourceViewer} to supply a custom source viewer,</li>
3353 	 * <li>{@linkplain #getSelectionProvider() getSelectionProvider} to supply a
3354 	 * custom selection provider.</li>
3355 	 * </ul>
3356 	 * <br>
3357 	 * Subclasses may extend the following methods called from within
3358 	 * <code>createPartControl</code>:
3359 	 * <ul>
3360 	 * <li>{@linkplain #initializeViewerColors(ISourceViewer)
3361 	 * initializeViewerColors} to customize the viewer color scheme (may also be
3362 	 * replaced),</li>
3363 	 * <li>{@linkplain #initializeDragAndDrop(ISourceViewer)
3364 	 * initializeDragAndDrop} to customize drag and drop (may also be
3365 	 * replaced),</li>
3366 	 * <li>{@linkplain #createNavigationActions() createNavigationActions} to
3367 	 * add navigation actions,</li>
3368 	 * <li>{@linkplain #createActions() createActions} to add text editor
3369 	 * actions.</li>
3370 	 * </ul>
3371 	 *
3372 	 * @param parent
3373 	 *            the parent composite
3374 	 */
3375 	@Override
createPartControl(Composite parent)3376 	public void createPartControl(Composite parent) {
3377 
3378 		fVerticalRuler= createVerticalRuler();
3379 
3380 		int styles= SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
3381 		fSourceViewer= createSourceViewer(parent, fVerticalRuler, styles);
3382 
3383 		if (fConfiguration == null)
3384 			fConfiguration= new SourceViewerConfiguration();
3385 		fSourceViewer.configure(fConfiguration);
3386 
3387 		if (fSourceViewer instanceof ISourceViewerExtension4)
3388 			fKeyBindingSupportForContentAssistant= new KeyBindingSupportForAssistant(((ISourceViewerExtension4)fSourceViewer));
3389 
3390 		if (fSourceViewer instanceof ISourceViewerExtension3) {
3391 			IQuickAssistAssistant assistant= ((ISourceViewerExtension3)fSourceViewer).getQuickAssistAssistant();
3392 			if (assistant != null)
3393 				fKeyBindingSupportForQuickAssistant= new KeyBindingSupportForAssistant(assistant);
3394 		}
3395 
3396 		if (fRangeIndicator != null)
3397 			fSourceViewer.setRangeIndicator(fRangeIndicator);
3398 
3399 		fSourceViewer.addTextListener(fTextListener);
3400 		fSourceViewer.addTextInputListener(fTextListener);
3401 		getSelectionProvider().addSelectionChangedListener(getSelectionChangedListener());
3402 
3403 		initializeViewerFont(fSourceViewer);
3404 		initializeViewerColors(fSourceViewer);
3405 		initializeFindScopeColor(fSourceViewer);
3406 		initializeDragAndDrop(fSourceViewer);
3407 
3408 		StyledText styledText= fSourceViewer.getTextWidget();
3409 		styledText.addMouseListener(getCursorListener());
3410 		styledText.addKeyListener(getCursorListener());
3411 
3412 		// Disable orientation switching until we fully support it.
3413 		styledText.addListener(SWT.OrientationChange, event -> event.doit = false);
3414 
3415 		if (getHelpContextId() != null)
3416 			PlatformUI.getWorkbench().getHelpSystem().setHelp(styledText, getHelpContextId());
3417 
3418 
3419 		String id= fEditorContextMenuId != null ?  fEditorContextMenuId : DEFAULT_EDITOR_CONTEXT_MENU_ID;
3420 
3421 		MenuManager manager= new MenuManager(id, id);
3422 		manager.setRemoveAllWhenShown(true);
3423 		manager.addMenuListener(getContextMenuListener());
3424 		fTextContextMenu= manager.createContextMenu(styledText);
3425 
3426 		styledText.setMenu(fTextContextMenu);
3427 
3428 		if (fEditorContextMenuId != null)
3429 			getEditorSite().registerContextMenu(fEditorContextMenuId, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());
3430 		else if (fCompatibilityMode)
3431 			getEditorSite().registerContextMenu(DEFAULT_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), isEditorInputIncludedInContextMenu());
3432 
3433 		if ((fEditorContextMenuId != null && fCompatibilityMode) || fEditorContextMenuId  == null) {
3434 			String partId= getEditorSite().getId();
3435 			if (partId != null)
3436 				getEditorSite().registerContextMenu(partId + ".EditorContext", manager, getSelectionProvider(), isEditorInputIncludedInContextMenu()); //$NON-NLS-1$
3437 		}
3438 
3439 		getEditorSite().registerContextMenu(COMMON_EDITOR_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);
3440 
3441 		if (fEditorContextMenuId == null)
3442 			fEditorContextMenuId= DEFAULT_EDITOR_CONTEXT_MENU_ID;
3443 
3444 
3445 		id= fRulerContextMenuId != null ? fRulerContextMenuId : DEFAULT_RULER_CONTEXT_MENU_ID;
3446 		manager= new MenuManager(id, id);
3447 		manager.setRemoveAllWhenShown(true);
3448 		manager.addMenuListener(getContextMenuListener());
3449 
3450 		Control rulerControl= fVerticalRuler.getControl();
3451 		fRulerContextMenu= manager.createContextMenu(rulerControl);
3452 		rulerControl.setMenu(fRulerContextMenu);
3453 		rulerControl.addMouseListener(getRulerMouseListener());
3454 
3455 		if (fRulerContextMenuId != null)
3456 			getEditorSite().registerContextMenu(fRulerContextMenuId, manager, getSelectionProvider(), false);
3457 		else if (fCompatibilityMode)
3458 			getEditorSite().registerContextMenu(DEFAULT_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);
3459 
3460 		if ((fRulerContextMenuId != null && fCompatibilityMode) || fRulerContextMenuId  == null) {
3461 			String partId= getSite().getId();
3462 			if (partId != null)
3463 				getEditorSite().registerContextMenu(partId + ".RulerContext", manager, getSelectionProvider(), false); //$NON-NLS-1$
3464 		}
3465 
3466 		getEditorSite().registerContextMenu(COMMON_RULER_CONTEXT_MENU_ID, manager, getSelectionProvider(), false);
3467 
3468 		if (fRulerContextMenuId == null)
3469 			fRulerContextMenuId= DEFAULT_RULER_CONTEXT_MENU_ID;
3470 
3471 		initializeZoomGestures(rulerControl, fSourceViewer);
3472 
3473 		getSite().setSelectionProvider(getSelectionProvider());
3474 
3475 		fSelectionListener= new SelectionListener();
3476 		fSelectionListener.install(getSelectionProvider());
3477 		fSelectionListener.setDocument(getDocumentProvider().getDocument(getEditorInput()));
3478 
3479 		initializeActivationCodeTrigger();
3480 
3481 		createNavigationActions();
3482 		createAccessibilityActions();
3483 		createActions();
3484 
3485 		initializeSourceViewer(getEditorInput());
3486 
3487 		/* since 3.2 - undo redo actions should be created after
3488 		 * the source viewer is initialized, so that the undo manager
3489 		 * can obtain its undo context from its document.
3490 		 */
3491 		createUndoRedoActions();
3492 
3493 		JFaceResources.getFontRegistry().addListener(fFontPropertyChangeListener);
3494 
3495 		IVerticalRuler ruler= getVerticalRuler();
3496 		if (ruler instanceof CompositeRuler)
3497 			updateContributedRulerColumns((CompositeRuler) ruler);
3498 
3499 		if (isWordWrapSupported()) {
3500 			setWordWrap(getInitialWordWrapStatus());
3501 		}
3502 	}
3503 
3504 	/**
3505 	 * Installs text drag and drop on the given source viewer.
3506 	 *
3507 	 * @param viewer the viewer
3508 	 * @since 3.3
3509 	 */
installTextDragAndDrop(final ISourceViewer viewer)3510 	protected void installTextDragAndDrop(final ISourceViewer viewer) {
3511 		if (viewer == null || fIsTextDragAndDropInstalled)
3512 			return;
3513 
3514 		final IDragAndDropService dndService= getSite().getService(IDragAndDropService.class);
3515 		if (dndService == null)
3516 			return;
3517 
3518 		final StyledText st= viewer.getTextWidget();
3519 
3520 		// Install drag source
3521 		final ISelectionProvider selectionProvider= viewer.getSelectionProvider();
3522 		final DragSource source= new DragSource(st, DND.DROP_COPY | DND.DROP_MOVE);
3523 		source.setTransfer(TextTransfer.getInstance());
3524 		source.addDragListener(new DragSourceAdapter() {
3525 			String fSelectedText;
3526 			Point fSelection;
3527 			@Override
3528 			public void dragStart(DragSourceEvent event) {
3529 				fTextDragAndDropToken= null;
3530 				try {
3531 					fSelection= st.getSelection();
3532 					event.doit= isLocationSelected(new Point(event.x, event.y));
3533 
3534 					ISelection selection= selectionProvider.getSelection();
3535 					if (selection instanceof ITextSelection)
3536 						fSelectedText= ((ITextSelection)selection).getText();
3537 					else // fallback to widget
3538 						fSelectedText= st.getSelectionText();
3539 				} catch (IllegalArgumentException ex) {
3540 					event.doit= false;
3541 				}
3542 			}
3543 
3544 			private boolean isLocationSelected(Point point) {
3545 				// FIXME: https://bugs.eclipse.org/bugs/show_bug.cgi?id=260922
3546 				if (isBlockSelectionModeEnabled())
3547 					return false;
3548 
3549 				int offset = st.getOffsetAtPoint(point);
3550 				Point p= st.getLocationAtOffset(offset);
3551 				if (p.x > point.x)
3552 					offset--;
3553 				return offset >= fSelection.x && offset < fSelection.y;
3554 			}
3555 
3556 			@Override
3557 			public void dragSetData(DragSourceEvent event) {
3558 				event.data= fSelectedText;
3559 				fTextDragAndDropToken= this; // Can be any non-null object
3560 			}
3561 
3562 			@Override
3563 			public void dragFinished(DragSourceEvent event) {
3564 				try {
3565 					if (event.detail == DND.DROP_MOVE && validateEditorInputState()) {
3566 						Point newSelection= st.getSelection();
3567 						int length= fSelection.y - fSelection.x;
3568 						int delta= 0;
3569 						if (newSelection.x < fSelection.x)
3570 							delta= length;
3571 						st.replaceTextRange(fSelection.x + delta, length, ""); //$NON-NLS-1$
3572 
3573 						if (fTextDragAndDropToken == null) {
3574 							// Move in same editor - end compound change
3575 							IRewriteTarget target= getAdapter(IRewriteTarget.class);
3576 							if (target != null)
3577 								target.endCompoundChange();
3578 						}
3579 
3580 					}
3581 				} finally {
3582 					fTextDragAndDropToken= null;
3583 				}
3584 			}
3585 		});
3586 
3587 		// Install drag target
3588 		DropTargetListener dropTargetListener= new DropTargetAdapter() {
3589 
3590 			private Point fSelection;
3591 
3592 			@Override
3593 			public void dragEnter(DropTargetEvent event) {
3594 				fTextDragAndDropToken= null;
3595 				fSelection= st.getSelection();
3596 				if (event.detail == DND.DROP_DEFAULT) {
3597 					if ((event.operations & DND.DROP_MOVE) != 0) {
3598 						event.detail= DND.DROP_MOVE;
3599 					} else if ((event.operations & DND.DROP_COPY) != 0) {
3600 						event.detail= DND.DROP_COPY;
3601 					} else {
3602 						event.detail= DND.DROP_NONE;
3603 					}
3604 				}
3605 			}
3606 
3607 			@Override
3608 			public void dragOperationChanged(DropTargetEvent event) {
3609 				if (event.detail == DND.DROP_DEFAULT) {
3610 					if ((event.operations & DND.DROP_MOVE) != 0) {
3611 						event.detail= DND.DROP_MOVE;
3612 					} else if ((event.operations & DND.DROP_COPY) != 0) {
3613 						event.detail= DND.DROP_COPY;
3614 					} else {
3615 						event.detail= DND.DROP_NONE;
3616 					}
3617 				}
3618 			}
3619 
3620 			@Override
3621 			public void dragOver(DropTargetEvent event) {
3622 				event.feedback |= DND.FEEDBACK_SCROLL;
3623 			}
3624 
3625 			@Override
3626 			public void drop(DropTargetEvent event) {
3627 				try {
3628 					if (fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
3629 						// Move in same editor
3630 						int caretOffset= st.getCaretOffset();
3631 						if (fSelection.x <= caretOffset && caretOffset <= fSelection.y) {
3632 							event.detail= DND.DROP_NONE;
3633 							return;
3634 						}
3635 
3636 						// Start compound change
3637 						IRewriteTarget target= getAdapter(IRewriteTarget.class);
3638 						if (target != null)
3639 							target.beginCompoundChange();
3640 					}
3641 
3642 					if (!validateEditorInputState()) {
3643 						event.detail= DND.DROP_NONE;
3644 						return;
3645 					}
3646 
3647 					String text= (String)event.data;
3648 					if (isBlockSelectionModeEnabled()) {
3649 						// FIXME fix block selection and DND
3650 //						if (fTextDNDColumnSelection != null && fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) {
3651 //							// DND_MOVE within same editor - remove origin before inserting
3652 //							Rectangle newSelection= st.getColumnSelection();
3653 //							st.replaceColumnSelection(fTextDNDColumnSelection, ""); //$NON-NLS-1$
3654 //							st.replaceColumnSelection(newSelection, text);
3655 //							st.setColumnSelection(newSelection.x, newSelection.y, newSelection.x + fTextDNDColumnSelection.width - fTextDNDColumnSelection.x, newSelection.y + fTextDNDColumnSelection.height - fTextDNDColumnSelection.y);
3656 //						} else {
3657 //							Point newSelection= st.getSelection();
3658 //							st.insert(text);
3659 //							IDocument document= getDocumentProvider().getDocument(getEditorInput());
3660 //							int startLine= st.getLineAtOffset(newSelection.x);
3661 //							int startColumn= newSelection.x - st.getOffsetAtLine(startLine);
3662 //							int endLine= startLine + document.computeNumberOfLines(text);
3663 //							int endColumn= startColumn + TextUtilities.indexOf(document.getLegalLineDelimiters(), text, 0)[0];
3664 //							st.setColumnSelection(startColumn, startLine, endColumn, endLine);
3665 //						}
3666 					} else {
3667 						Point newSelection= st.getSelection();
3668 						try {
3669 							int modelOffset= widgetOffset2ModelOffset(viewer, newSelection.x);
3670 							viewer.getDocument().replace(modelOffset, 0, text);
3671 						} catch (BadLocationException e) {
3672 							return;
3673 						}
3674 						st.setSelectionRange(newSelection.x, text.length());
3675 					}
3676 				} finally {
3677 					fTextDragAndDropToken= null;
3678 				}
3679 			}
3680 		};
3681 		dndService.addMergedDropTarget(st, DND.DROP_MOVE | DND.DROP_COPY, new Transfer[] {TextTransfer.getInstance()}, dropTargetListener);
3682 
3683 		fIsTextDragAndDropInstalled= true;
3684 	}
3685 
3686 	/**
3687 	 * Uninstalls text drag and drop from the given source viewer.
3688 	 *
3689 	 * @param viewer the viewer
3690 	 * @since 3.3
3691 	 */
uninstallTextDragAndDrop(ISourceViewer viewer)3692 	protected void uninstallTextDragAndDrop(ISourceViewer viewer) {
3693 		if (viewer == null || !fIsTextDragAndDropInstalled)
3694 			return;
3695 
3696 		final IDragAndDropService dndService= getSite().getService(IDragAndDropService.class);
3697 		if (dndService == null)
3698 			return;
3699 
3700 		StyledText st= viewer.getTextWidget();
3701 		dndService.removeMergedDropTarget(st);
3702 
3703 		DragSource dragSource= (DragSource)st.getData(DND.DRAG_SOURCE_KEY);
3704 		if (dragSource != null) {
3705 			dragSource.dispose();
3706 			st.setData(DND.DRAG_SOURCE_KEY, null);
3707 		}
3708 
3709 		fIsTextDragAndDropInstalled= false;
3710 	}
3711 
3712 	/**
3713 	 * Tells whether the editor input should be included when adding object
3714 	 * contributions to this editor's context menu.
3715 	 * <p>
3716 	 * This implementation always returns <code>true</code>.
3717 	 * </p>
3718 	 *
3719 	 * @return <code>true</code> if the editor input should be considered
3720 	 * @since 3.2
3721 	 */
isEditorInputIncludedInContextMenu()3722 	protected boolean isEditorInputIncludedInContextMenu() {
3723 		return true;
3724 	}
3725 
3726 	/**
3727 	 * Initializes the activation code trigger.
3728 	 *
3729 	 * @since 2.1
3730 	 */
initializeActivationCodeTrigger()3731 	private void initializeActivationCodeTrigger() {
3732 		fActivationCodeTrigger.install();
3733 		fActivationCodeTrigger.setScopes(fKeyBindingScopes);
3734 	}
3735 
3736 	/**
3737 	 * Initializes the given viewer's font.
3738 	 *
3739 	 * @param viewer the viewer
3740 	 * @since 2.0
3741 	 */
initializeViewerFont(ISourceViewer viewer)3742 	private void initializeViewerFont(ISourceViewer viewer) {
3743 
3744 		boolean isSharedFont= true;
3745 		Font font= null;
3746 		String symbolicFontName= getSymbolicFontName();
3747 
3748 		if (symbolicFontName != null)
3749 			font= JFaceResources.getFont(symbolicFontName);
3750 		else if (fPreferenceStore != null) {
3751 			// Backward compatibility
3752 			if (fPreferenceStore.contains(JFaceResources.TEXT_FONT) && !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) {
3753 				FontData data= PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT);
3754 
3755 				if (data != null) {
3756 					isSharedFont= false;
3757 					font= new Font(viewer.getTextWidget().getDisplay(), data);
3758 				}
3759 			}
3760 		}
3761 		if (font == null)
3762 			font= JFaceResources.getTextFont();
3763 
3764 		if (!font.equals(fSourceViewer.getTextWidget().getFont())) {
3765 			setFont(viewer, font);
3766 
3767 			disposeFont();
3768 			if (!isSharedFont)
3769 				fFont= font;
3770 		} else if (!isSharedFont) {
3771 			font.dispose();
3772 		}
3773 	}
3774 
3775 	/**
3776 	 * Disposes of the non-shared font.
3777 	 *
3778 	 * @since 3.5
3779 	 */
disposeFont()3780 	private void disposeFont() {
3781 		if (fFont != null) {
3782 			fFont.dispose();
3783 			fFont= null;
3784 		}
3785 	}
3786 
3787 	/**
3788 	 * Sets the font for the given viewer sustaining selection and scroll position.
3789 	 *
3790 	 * @param sourceViewer the source viewer
3791 	 * @param font the font
3792 	 * @since 2.0
3793 	 */
setFont(ISourceViewer sourceViewer, Font font)3794 	private void setFont(ISourceViewer sourceViewer, Font font) {
3795 		if (sourceViewer.getDocument() != null) {
3796 
3797 			ISelectionProvider provider= sourceViewer.getSelectionProvider();
3798 			ISelection selection= provider.getSelection();
3799 			int topIndex= sourceViewer.getTopIndex();
3800 
3801 			StyledText styledText= sourceViewer.getTextWidget();
3802 			Control parent= styledText;
3803 			if (sourceViewer instanceof ITextViewerExtension) {
3804 				ITextViewerExtension extension= (ITextViewerExtension) sourceViewer;
3805 				parent= extension.getControl();
3806 			}
3807 
3808 			parent.setRedraw(false);
3809 
3810 			styledText.setFont(font);
3811 
3812 			if (fVerticalRuler instanceof IVerticalRulerExtension) {
3813 				IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
3814 				e.setFont(font);
3815 			}
3816 
3817 			provider.setSelection(selection);
3818 			sourceViewer.setTopIndex(topIndex);
3819 
3820 			if (parent instanceof Composite) {
3821 				Composite composite= (Composite) parent;
3822 				composite.layout(true);
3823 			}
3824 
3825 			parent.setRedraw(true);
3826 
3827 
3828 		} else {
3829 
3830 			StyledText styledText= sourceViewer.getTextWidget();
3831 			styledText.setFont(font);
3832 
3833 			if (fVerticalRuler instanceof IVerticalRulerExtension) {
3834 				IVerticalRulerExtension e= (IVerticalRulerExtension) fVerticalRuler;
3835 				e.setFont(font);
3836 			}
3837 		}
3838 	}
3839 
initializeZoomGestures(Control rulerControl, final ISourceViewer sourceViewer)3840 	private void initializeZoomGestures(Control rulerControl, final ISourceViewer sourceViewer) {
3841 		final StyledText styledText= sourceViewer.getTextWidget();
3842 		GestureListener gestureListener= new GestureListener() {
3843 			private Font fMagnificationStartFont;
3844 			private int fLastHeight= -1;
3845 
3846 			@Override
3847 			public void gesture(GestureEvent e) {
3848 				if (e.detail == SWT.GESTURE_BEGIN) {
3849 					fMagnificationStartFont= styledText.getFont();
3850 				} else if (e.detail == SWT.GESTURE_END) {
3851 					fMagnificationStartFont= null;
3852 					updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
3853 				} else if (e.detail == SWT.GESTURE_ROTATE) {
3854 					if (Math.abs(e.rotation) > 45) {
3855 						fMagnificationStartFont= null; // don't observe magnify events after reset
3856 						initializeViewerFont(fSourceViewer);
3857 						updateCaret();
3858 						IStatusField statusField= getStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
3859 						if (statusField != null) {
3860 							int newHeight= styledText.getFont().getFontData()[0].getHeight();
3861 							statusField.setText(NLSUtility.format(EditorMessages.Editor_font_reset_message, Integer.valueOf(newHeight)));
3862 						}
3863 					}
3864 				} else if (e.detail == SWT.GESTURE_MAGNIFY && fMagnificationStartFont != null) {
3865 					FontData fontData= fMagnificationStartFont.getFontData()[0];
3866 					int startHeight= fontData.getHeight();
3867 					int newHeight= Math.max(1, (int) (startHeight * e.magnification));
3868 					if (newHeight != fLastHeight) {
3869 						fLastHeight= newHeight;
3870 						fontData.setHeight(newHeight);
3871 						Font newFont= new Font(fMagnificationStartFont.getDevice(), fontData);
3872 						setFont(sourceViewer, newFont);
3873 						disposeFont();
3874 						updateCaret();
3875 						IStatusField statusField= getStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
3876 						if (statusField != null) {
3877 							statusField.setText(NLSUtility.format(EditorMessages.Editor_font_zoom_message, new Object[] { Integer.valueOf(startHeight), Integer.valueOf(newHeight) }));
3878 						}
3879 					}
3880 				}
3881 			}
3882 		};
3883 		styledText.addGestureListener(gestureListener);
3884 		rulerControl.addGestureListener(gestureListener);
3885 	}
3886 
3887 	/**
3888 	 * Creates a color from the information stored in the given preference store.
3889 	 * Returns <code>null</code> if there is no such information available.
3890 	 *
3891 	 * @param store the store to read from
3892 	 * @param key the key used for the lookup in the preference store
3893 	 * @param display the display used create the color
3894 	 * @return the created color according to the specification in the preference store
3895 	 * @since 2.0
3896 	 */
createColor(IPreferenceStore store, String key, Display display)3897 	private Color createColor(IPreferenceStore store, String key, Display display) {
3898 
3899 		RGB rgb= null;
3900 
3901 		if (store.contains(key)) {
3902 
3903 			if (store.isDefault(key))
3904 				rgb= PreferenceConverter.getDefaultColor(store, key);
3905 			else
3906 				rgb= PreferenceConverter.getColor(store, key);
3907 
3908 			if (rgb != null)
3909 				return new Color(display, rgb);
3910 		}
3911 
3912 		return null;
3913 	}
3914 
3915 	/**
3916 	 * Initializes the fore- and background colors of the given viewer for both
3917 	 * normal and selected text.
3918 	 *
3919 	 * @param viewer the viewer to be initialized
3920 	 * @since 2.0
3921 	 */
initializeViewerColors(ISourceViewer viewer)3922 	protected void initializeViewerColors(ISourceViewer viewer) {
3923 
3924 		IPreferenceStore store= getPreferenceStore();
3925 		if (store != null) {
3926 
3927 			StyledText styledText= viewer.getTextWidget();
3928 
3929 			// ----------- foreground color --------------------
3930 			Color color= store.getBoolean(PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
3931 				? null
3932 				: createColor(store, PREFERENCE_COLOR_FOREGROUND, styledText.getDisplay());
3933 			styledText.setForeground(color);
3934 
3935 			if (fForegroundColor != null)
3936 				fForegroundColor.dispose();
3937 
3938 			fForegroundColor= color;
3939 
3940 			// ---------- background color ----------------------
3941 			color= store.getBoolean(PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
3942 				? null
3943 				: createColor(store, PREFERENCE_COLOR_BACKGROUND, styledText.getDisplay());
3944 			styledText.setBackground(color);
3945 
3946 			if (fBackgroundColor != null)
3947 				fBackgroundColor.dispose();
3948 
3949 			fBackgroundColor= color;
3950 
3951 			// ----------- selection foreground color --------------------
3952 			color= store.getBoolean(PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)
3953 				? null
3954 				: createColor(store, PREFERENCE_COLOR_SELECTION_FOREGROUND, styledText.getDisplay());
3955 			styledText.setSelectionForeground(color);
3956 
3957 			if (fSelectionForegroundColor != null)
3958 				fSelectionForegroundColor.dispose();
3959 
3960 			fSelectionForegroundColor= color;
3961 
3962 			// ---------- selection background color ----------------------
3963 			color= store.getBoolean(PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)
3964 				? null
3965 				: createColor(store, PREFERENCE_COLOR_SELECTION_BACKGROUND, styledText.getDisplay());
3966 			styledText.setSelectionBackground(color);
3967 
3968 			if (fSelectionBackgroundColor != null)
3969 				fSelectionBackgroundColor.dispose();
3970 
3971 			fSelectionBackgroundColor= color;
3972 		}
3973 	}
3974 
3975 	/**
3976 	 * Initializes the background color used for highlighting the document ranges
3977 	 * defining search scopes.
3978 	 *
3979 	 * @param viewer the viewer to initialize
3980 	 * @since 2.0
3981 	 */
initializeFindScopeColor(ISourceViewer viewer)3982 	private void initializeFindScopeColor(ISourceViewer viewer) {
3983 
3984 		IPreferenceStore store= getPreferenceStore();
3985 		if (store != null) {
3986 
3987 			StyledText styledText= viewer.getTextWidget();
3988 
3989 			Color color= createColor(store, PREFERENCE_COLOR_FIND_SCOPE, styledText.getDisplay());
3990 
3991 			IFindReplaceTarget target= viewer.getFindReplaceTarget();
3992 			if (target != null && target instanceof IFindReplaceTargetExtension)
3993 				((IFindReplaceTargetExtension) target).setScopeHighlightColor(color);
3994 
3995 			if (fFindScopeHighlightColor != null)
3996 				fFindScopeHighlightColor.dispose();
3997 
3998 			fFindScopeHighlightColor= color;
3999 		}
4000 	}
4001 
4002 
4003 	/**
4004 	 * Initializes the editor's source viewer based on the given editor input.
4005 	 *
4006 	 * @param input the editor input to be used to initialize the source viewer
4007 	 */
initializeSourceViewer(IEditorInput input)4008 	private void initializeSourceViewer(IEditorInput input) {
4009 
4010 		IDocumentProvider documentProvider= getDocumentProvider();
4011 		IAnnotationModel model= documentProvider.getAnnotationModel(input);
4012 		IDocument document= documentProvider.getDocument(input);
4013 
4014 		if (document != null) {
4015 			fSourceViewer.setDocument(document, model);
4016 			fSourceViewer.setEditable(isEditable());
4017 			fSourceViewer.showAnnotations(model != null);
4018 		}
4019 
4020 		if (fElementStateListener instanceof IElementStateListenerExtension) {
4021 			boolean isStateValidated= false;
4022 			if (documentProvider instanceof IDocumentProviderExtension)
4023 				isStateValidated= ((IDocumentProviderExtension)documentProvider).isStateValidated(input);
4024 
4025 			IElementStateListenerExtension extension= (IElementStateListenerExtension) fElementStateListener;
4026 			extension.elementStateValidationChanged(input, isStateValidated);
4027 		}
4028 
4029 		if (fInitialCaret == null)
4030 			fInitialCaret= fSourceViewer.getTextWidget().getCaret();
4031 
4032 		if (fIsOverwriting)
4033 			fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
4034 		handleInsertModeChanged();
4035 
4036 		if (isTabsToSpacesConversionEnabled())
4037 			installTabsToSpacesConverter();
4038 
4039 		if (fSourceViewer instanceof ITextViewerExtension8) {
4040 			IPreferenceStore store= getPreferenceStore();
4041 			EnrichMode mode= store != null ? convertEnrichModePreference(store.getInt(PREFERENCE_HOVER_ENRICH_MODE)) : EnrichMode.AFTER_DELAY;
4042 			((ITextViewerExtension8)fSourceViewer).setHoverEnrichMode(mode);
4043 		}
4044 
4045 		if (fSourceViewer instanceof ISourceViewerExtension5)
4046 			installCodeMiningProviders();
4047 	}
4048 
4049 	/**
4050 	 * Install codemining providers.
4051 	 *
4052 	 * @since 3.11
4053 	 */
installCodeMiningProviders()4054 	protected void installCodeMiningProviders() {
4055 		ICodeMiningProvider[] providers = TextEditorPlugin.getDefault().getCodeMiningProviderRegistry()
4056 				.getProviders(this.getSourceViewer(), this);
4057 		((ISourceViewerExtension5) fSourceViewer).setCodeMiningProviders(providers);
4058 	}
4059 
4060 	/**
4061 	 * Converts the {link #PREFERENCE_HOVER_ENRICH_MODE} preference value to
4062 	 * {@link org.eclipse.jface.text.ITextViewerExtension8.EnrichMode}.
4063 	 *
4064 	 * @param mode the preference value
4065 	 * @return the enrich mode, can be <code>null</code>
4066 	 * @since 3.4
4067 	 */
convertEnrichModePreference(int mode)4068 	private EnrichMode convertEnrichModePreference(int mode) {
4069 		switch (mode) {
4070 			case -1:
4071 				return null;
4072 			case 0:
4073 				return EnrichMode.AFTER_DELAY;
4074 			case 1:
4075 				return EnrichMode.IMMEDIATELY;
4076 			case 2:
4077 				return EnrichMode.ON_CLICK;
4078 			default:
4079 				Assert.isLegal(false);
4080 			return null;
4081 		}
4082 	}
4083 
4084 	/**
4085 	 * Initializes the editor's title based on the given editor input.
4086 	 * <p>
4087 	 * <strong>Note:</strong> We use the editor's image instead of the image from the
4088 	 * editor input to distinguish situations where the same editor input is
4089 	 * opened in different kinds of editors.
4090 	 * </p>
4091 	 *
4092 	 * @param input the editor input to be used
4093 	 */
initializeTitle(IEditorInput input)4094 	private void initializeTitle(IEditorInput input) {
4095 
4096 		Image oldImage= fTitleImage;
4097 		fTitleImage= null;
4098 		String title= ""; //$NON-NLS-1$
4099 
4100 		if (input != null) {
4101 			IEditorRegistry editorRegistry= PlatformUI.getWorkbench().getEditorRegistry();
4102 			IEditorDescriptor editorDesc= editorRegistry.findEditor(getSite().getId());
4103 			ImageDescriptor imageDesc= editorDesc != null ? editorDesc.getImageDescriptor() : null;
4104 
4105 			fTitleImage= imageDesc != null ? imageDesc.createImage() : null;
4106 			title= input.getName();
4107 		}
4108 
4109 		setTitleImage(fTitleImage);
4110 		setPartName(title);
4111 
4112 		firePropertyChange(PROP_DIRTY);
4113 
4114 		if (oldImage != null && !oldImage.isDisposed())
4115 			oldImage.dispose();
4116 	}
4117 
4118 	/**
4119 	 * Hook method for setting the document provider for the given input.
4120 	 * This default implementation does nothing. Clients may
4121 	 * reimplement.
4122 	 *
4123 	 * @param input the input of this editor.
4124 	 * @since 3.0
4125 	 */
setDocumentProvider(IEditorInput input)4126 	protected void setDocumentProvider(IEditorInput input) {
4127 	}
4128 
4129 	/**
4130 	 * If there is no explicit document provider set, the implicit one is
4131 	 * re-initialized based on the given editor input.
4132 	 *
4133 	 * @param input the editor input.
4134 	 */
updateDocumentProvider(IEditorInput input)4135 	private void updateDocumentProvider(IEditorInput input) {
4136 
4137 		IProgressMonitor rememberedProgressMonitor= null;
4138 
4139 		IDocumentProvider provider= getDocumentProvider();
4140 		if (provider != null) {
4141 			provider.removeElementStateListener(fElementStateListener);
4142 			if (provider instanceof IDocumentProviderExtension2) {
4143 				IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
4144 				rememberedProgressMonitor= extension.getProgressMonitor();
4145 				extension.setProgressMonitor(null);
4146 			}
4147 		}
4148 
4149 		setDocumentProvider(input);
4150 
4151 		provider= getDocumentProvider();
4152 		if (provider != null) {
4153 			provider.addElementStateListener(fElementStateListener);
4154 			if (provider instanceof IDocumentProviderExtension2) {
4155 				IDocumentProviderExtension2 extension= (IDocumentProviderExtension2) provider;
4156 				extension.setProgressMonitor(rememberedProgressMonitor);
4157 			}
4158 		}
4159 	}
4160 
4161 	/**
4162 	 * Called directly from <code>setInput</code> and from within a workspace
4163 	 * runnable from <code>init</code>, this method does the actual setting
4164 	 * of the editor input. Closes the editor if <code>input</code> is
4165 	 * <code>null</code>. Disconnects from any previous editor input and its
4166 	 * document provider and connects to the new one.
4167 	 * <p>
4168 	 * Subclasses may extend.
4169 	 * </p>
4170 	 *
4171 	 * @param input the input to be set
4172 	 * @exception CoreException if input cannot be connected to the document
4173 	 *            provider
4174 	 */
doSetInput(IEditorInput input)4175 	protected void doSetInput(IEditorInput input) throws CoreException {
4176 		ISaveablesLifecycleListener listener= getSite().getService(ISaveablesLifecycleListener.class);
4177 		if (listener == null)
4178 			fSavable= null;
4179 
4180 		if (input == null) {
4181 			close(isSaveOnCloseNeeded());
4182 
4183 			if (fSavable != null) {
4184 				listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this,	SaveablesLifecycleEvent.POST_CLOSE,	getSaveables(), false));
4185 				fSavable.disconnectEditor();
4186 				fSavable= null;
4187 			}
4188 
4189 		} else {
4190 			boolean mustSendLifeCycleEvent= false;
4191 			if (fSavable != null) {
4192 				listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this,	SaveablesLifecycleEvent.POST_CLOSE,	getSaveables(), false));
4193 				fSavable.disconnectEditor();
4194 				fSavable= null;
4195 				mustSendLifeCycleEvent= true;
4196 			}
4197 
4198 			IEditorInput oldInput= getEditorInput();
4199 			if (oldInput != null)
4200 				getDocumentProvider().disconnect(oldInput);
4201 
4202 			super.setInput(input);
4203 
4204 			updateDocumentProvider(input);
4205 
4206 			IDocumentProvider provider= getDocumentProvider();
4207 			if (provider == null) {
4208 				IStatus s= new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_no_provider, null);
4209 				throw new CoreException(s);
4210 			}
4211 
4212 			provider.connect(input);
4213 
4214 			initializeTitle(input);
4215 
4216 			if (fSourceViewer != null) {
4217 				initializeSourceViewer(input);
4218 
4219 				// Reset the undo context for the undo and redo action handlers
4220 				IAction undoAction= getAction(ITextEditorActionConstants.UNDO);
4221 				IAction redoAction= getAction(ITextEditorActionConstants.REDO);
4222 				boolean areOperationActionHandlersInstalled= undoAction instanceof OperationHistoryActionHandler && redoAction instanceof OperationHistoryActionHandler;
4223 				IUndoContext undoContext= getUndoContext();
4224 				if (undoContext != null && areOperationActionHandlersInstalled) {
4225 					((OperationHistoryActionHandler)undoAction).setContext(undoContext);
4226 					((OperationHistoryActionHandler)redoAction).setContext(undoContext);
4227 				} else {
4228 					createUndoRedoActions();
4229 				}
4230 			}
4231 
4232 			if (fIsOverwriting)
4233 				toggleOverwriteMode();
4234 			setInsertMode(getLegalInsertModes().get(0));
4235 			updateCaret();
4236 
4237 			updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
4238 
4239 			if (fSelectionListener != null)
4240 				fSelectionListener.setDocument(getDocumentProvider().getDocument(input));
4241 
4242 			IVerticalRuler ruler= getVerticalRuler();
4243 			if (ruler instanceof CompositeRuler)
4244 				updateContributedRulerColumns((CompositeRuler) ruler);
4245 
4246 			// Send savable life-cycle if needed.
4247 			if (mustSendLifeCycleEvent && listener != null)
4248 				listener.handleLifecycleEvent(new SaveablesLifecycleEvent(this,	SaveablesLifecycleEvent.POST_OPEN, getSaveables(), false));
4249 
4250 		}
4251 
4252 	}
4253 
4254 	/**
4255 	 * Returns this editor's viewer's undo manager undo context.
4256 	 *
4257 	 * @return the undo context or <code>null</code> if not available
4258 	 * @since 3.1
4259 	 */
getUndoContext()4260 	private IUndoContext getUndoContext() {
4261 		if (fSourceViewer instanceof ITextViewerExtension6) {
4262 			IUndoManager undoManager= ((ITextViewerExtension6)fSourceViewer).getUndoManager();
4263 			if (undoManager instanceof IUndoManagerExtension)
4264 				return ((IUndoManagerExtension)undoManager).getUndoContext();
4265 		}
4266 		return null;
4267 	}
4268 
4269 	@Override
setInputWithNotify(IEditorInput input)4270 	protected final void setInputWithNotify(IEditorInput input) {
4271 		try {
4272 
4273 			doSetInput(input);
4274 
4275 			/*
4276 			 * The following bugs explain why we fire this property change:
4277 			 * 	https://bugs.eclipse.org/bugs/show_bug.cgi?id=90283
4278 			 * 	https://bugs.eclipse.org/bugs/show_bug.cgi?id=92049
4279 			 * 	https://bugs.eclipse.org/bugs/show_bug.cgi?id=92286
4280 			 */
4281 			firePropertyChange(IEditorPart.PROP_INPUT);
4282 
4283 		} catch (CoreException x) {
4284 			String title= EditorMessages.Editor_error_setinput_title;
4285 			String msg= EditorMessages.Editor_error_setinput_message;
4286 			Shell shell= getSite().getShell();
4287 			ErrorDialog.openError(shell, title, msg, x.getStatus());
4288 		}
4289 	}
4290 
4291 	@Override
setInput(IEditorInput input)4292 	public final void setInput(IEditorInput input) {
4293 		setInputWithNotify(input);
4294 	}
4295 
4296 	/*
4297 	 * @see ITextEditor#close
4298 	 */
4299 	@Override
close(final boolean save)4300 	public void close(final boolean save) {
4301 
4302 		enableSanityChecking(false);
4303 
4304 		Display display= getSite().getShell().getDisplay();
4305 		display.asyncExec(() -> {
4306 			if (fSourceViewer != null)
4307 				getSite().getPage().closeEditor(AbstractTextEditor.this, save);
4308 		});
4309 	}
4310 
4311 	/**
4312 	 * The <code>AbstractTextEditor</code> implementation of this
4313 	 * <code>IWorkbenchPart</code> method may be extended by subclasses.
4314 	 * Subclasses must call <code>super.dispose()</code>.
4315 	 * <p>
4316 	 * Note that many methods may return <code>null</code> after the editor is
4317 	 * disposed.
4318 	 * </p>
4319 	 */
4320 	@Override
dispose()4321 	public void dispose() {
4322 
4323 		if (fActivationListener != null) {
4324 			fActivationListener.dispose();
4325 			fActivationListener= null;
4326 		}
4327 
4328 		if (fTitleImage != null) {
4329 			fTitleImage.dispose();
4330 			fTitleImage= null;
4331 		}
4332 
4333 		disposeFont();
4334 
4335 		disposeNonDefaultCaret();
4336 		fInitialCaret= null;
4337 
4338 		if (fForegroundColor != null) {
4339 			fForegroundColor.dispose();
4340 			fForegroundColor= null;
4341 		}
4342 
4343 		if (fBackgroundColor != null) {
4344 			fBackgroundColor.dispose();
4345 			fBackgroundColor= null;
4346 		}
4347 
4348 		if (fSelectionForegroundColor != null) {
4349 			fSelectionForegroundColor.dispose();
4350 			fSelectionForegroundColor= null;
4351 		}
4352 
4353 		if (fSelectionBackgroundColor != null) {
4354 			fSelectionBackgroundColor.dispose();
4355 			fSelectionBackgroundColor= null;
4356 		}
4357 
4358 		if (fFindScopeHighlightColor != null) {
4359 			fFindScopeHighlightColor.dispose();
4360 			fFindScopeHighlightColor= null;
4361 		}
4362 
4363 		if (fFontPropertyChangeListener != null) {
4364 			JFaceResources.getFontRegistry().removeListener(fFontPropertyChangeListener);
4365 			if (fPreferenceStore != null)
4366 				fPreferenceStore.removePropertyChangeListener(fFontPropertyChangeListener);
4367 			fFontPropertyChangeListener= null;
4368 		}
4369 
4370 		if (fPropertyChangeListener != null) {
4371 			if (fPreferenceStore != null) {
4372 				fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
4373 				fPreferenceStore= null;
4374 			}
4375 			fPropertyChangeListener= null;
4376 		}
4377 
4378 		if (fActivationCodeTrigger != null) {
4379 			fActivationCodeTrigger.uninstall();
4380 			fActivationCodeTrigger= null;
4381 		}
4382 
4383 		if (fSelectionListener != null)  {
4384 			fSelectionListener.uninstall(getSelectionProvider());
4385 			fSelectionListener= null;
4386 		}
4387 
4388 		if (fSavable != null) {
4389 			fSavable.disconnectEditor();
4390 			fSavable= null;
4391 		}
4392 
4393 		disposeDocumentProvider();
4394 
4395 		if (fSourceViewer != null) {
4396 
4397 			if (fTextListener != null) {
4398 				fSourceViewer.removeTextListener(fTextListener);
4399 				fSourceViewer.removeTextInputListener(fTextListener);
4400 				fTextListener= null;
4401 			}
4402 
4403 			uninstallTabsToSpacesConverter();
4404 
4405 			fTextInputListener= null;
4406 			fSelectionProvider= null;
4407 			fSourceViewer= null;
4408 		}
4409 
4410 		if (fTextContextMenu != null) {
4411 			fTextContextMenu.dispose();
4412 			fTextContextMenu= null;
4413 		}
4414 
4415 		if (fRulerContextMenu != null) {
4416 			fRulerContextMenu.dispose();
4417 			fRulerContextMenu= null;
4418 		}
4419 
4420 		if (fActions != null) {
4421 			registerUndoRedoAction(ITextEditorActionConstants.UNDO, null);
4422 			registerUndoRedoAction(ITextEditorActionConstants.REDO, null);
4423 			fActions.clear();
4424 			fActions= null;
4425 		}
4426 
4427 		if (fSelectionActions != null) {
4428 			fSelectionActions.clear();
4429 			fSelectionActions= null;
4430 		}
4431 
4432 		if (fContentActions != null) {
4433 			fContentActions.clear();
4434 			fContentActions= null;
4435 		}
4436 
4437 		if (fPropertyActions != null) {
4438 			fPropertyActions.clear();
4439 			fPropertyActions= null;
4440 		}
4441 
4442 		if (fStateActions != null) {
4443 			fStateActions.clear();
4444 			fStateActions= null;
4445 		}
4446 
4447 		if (fActivationCodes != null) {
4448 			fActivationCodes.clear();
4449 			fActivationCodes= null;
4450 		}
4451 
4452 		if (fEditorStatusLine != null)
4453 			fEditorStatusLine= null;
4454 
4455 		if (fConfiguration != null)
4456 			fConfiguration= null;
4457 
4458 		if (fColumnSupport != null) {
4459 			fColumnSupport.dispose();
4460 			fColumnSupport= null;
4461 		}
4462 
4463 		if (fVerticalRuler != null)
4464 			fVerticalRuler= null;
4465 
4466 		IOperationHistory history= OperationHistoryFactory.getOperationHistory();
4467 		if (history != null) {
4468 			if (fNonLocalOperationApprover != null)
4469 				history.removeOperationApprover(fNonLocalOperationApprover);
4470 			if (fLinearUndoViolationApprover != null)
4471 				history.removeOperationApprover(fLinearUndoViolationApprover);
4472 		}
4473 		fNonLocalOperationApprover= null;
4474 		fLinearUndoViolationApprover= null;
4475 
4476 		if (fKeyBindingSupportForContentAssistant != null) {
4477 			fKeyBindingSupportForContentAssistant.dispose();
4478 			fKeyBindingSupportForContentAssistant= null;
4479 		}
4480 
4481 		if (fKeyBindingSupportForQuickAssistant != null) {
4482 			fKeyBindingSupportForQuickAssistant.dispose();
4483 			fKeyBindingSupportForQuickAssistant= null;
4484 		}
4485 
4486 		if (fInformationPresenter != null) {
4487 			fInformationPresenter.uninstall();
4488 			fInformationPresenter= null;
4489 		}
4490 
4491 		if (fSaveAction != null) {
4492 			fSaveAction.dispose();
4493 			fSaveAction= null;
4494 		}
4495 
4496 		super.dispose();
4497 	}
4498 
4499 	/**
4500 	 * Disposes of the connection with the document provider. Subclasses
4501 	 * may extend.
4502 	 *
4503 	 * @since 3.0
4504 	 */
disposeDocumentProvider()4505 	protected void disposeDocumentProvider() {
4506 		IDocumentProvider provider= getDocumentProvider();
4507 		if (provider != null) {
4508 
4509 			IEditorInput input= getEditorInput();
4510 			if (input != null)
4511 				provider.disconnect(input);
4512 
4513 			if (fElementStateListener != null) {
4514 				provider.removeElementStateListener(fElementStateListener);
4515 				fElementStateListener= null;
4516 			}
4517 
4518 		}
4519 		fExplicitDocumentProvider= null;
4520 	}
4521 
4522 	/**
4523 	 * Determines whether the given preference change affects the editor's
4524 	 * presentation. This implementation always returns <code>false</code>.
4525 	 * May be reimplemented by subclasses.
4526 	 *
4527 	 * @param event the event which should be investigated
4528 	 * @return <code>true</code> if the event describes a preference change affecting the editor's presentation
4529 	 * @since 2.0
4530 	 */
affectsTextPresentation(PropertyChangeEvent event)4531 	protected boolean affectsTextPresentation(PropertyChangeEvent event) {
4532 		return false;
4533 	}
4534 
4535 	/**
4536 	 * Returns the symbolic font name for this editor as defined in XML.
4537 	 *
4538 	 * @return a String with the symbolic font name or <code>null</code> if
4539 	 *         none is defined
4540 	 * @since 2.1
4541 	 */
getSymbolicFontName()4542 	/*package*/ String getSymbolicFontName() {
4543 		if (getConfigurationElement() != null)
4544 			return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$
4545 		return null;
4546 	}
4547 
4548 	/**
4549 	 * Returns the property preference key for the editor font.
4550 	 * <p>
4551 	 * If the editor is defined with a <code>symbolicFontName </code> then this name is returned and
4552 	 * the font is looked up in the JFace resource registry. Otherwise,
4553 	 * {@link JFaceResources#TEXT_FONT} is returned and the font is looked up in this editor's
4554 	 * preference store.
4555 	 * </p>
4556 	 *
4557 	 * @return a String with the key
4558 	 * @since 2.1
4559 	 */
getFontPropertyPreferenceKey()4560 	protected final String getFontPropertyPreferenceKey() {
4561 		String symbolicFontName= getSymbolicFontName();
4562 		if (symbolicFontName != null)
4563 			return symbolicFontName;
4564 		return JFaceResources.TEXT_FONT;
4565 	}
4566 
4567 	/**
4568 	 * Handles a property change event describing a change of the editor's
4569 	 * preference store and updates the preference related editor properties.
4570 	 * <p>
4571 	 * Subclasses may extend.
4572 	 * </p>
4573 	 *
4574 	 * @param event the property change event
4575 	 */
handlePreferenceStoreChanged(PropertyChangeEvent event)4576 	protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
4577 
4578 		if (fSourceViewer == null)
4579 			return;
4580 
4581 		String property= event.getProperty();
4582 
4583 		if (getFontPropertyPreferenceKey().equals(property))
4584 			// There is a separate handler for font preference changes
4585 			return;
4586 
4587 		if (property != null) {
4588 			switch (property) {
4589 			case PREFERENCE_COLOR_FOREGROUND:
4590 			case PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT:
4591 			case PREFERENCE_COLOR_BACKGROUND:
4592 			case PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT:
4593 			case PREFERENCE_COLOR_SELECTION_FOREGROUND:
4594 			case PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT:
4595 			case PREFERENCE_COLOR_SELECTION_BACKGROUND:
4596 			case PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT:
4597 				initializeViewerColors(fSourceViewer);
4598 				break;
4599 			case PREFERENCE_COLOR_FIND_SCOPE:
4600 				initializeFindScopeColor(fSourceViewer);
4601 				break;
4602 			case PREFERENCE_USE_CUSTOM_CARETS:
4603 			case PREFERENCE_WIDE_CARET:
4604 				updateCaret();
4605 				break;
4606 			default:
4607 				break;
4608 			}
4609 		}
4610 
4611 		if (affectsTextPresentation(event))
4612 			fSourceViewer.invalidateTextPresentation();
4613 
4614 		if (PREFERENCE_HYPERLINKS_ENABLED.equals(property)) {
4615 			if (fSourceViewer instanceof ITextViewerExtension6) {
4616 				IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
4617 				int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
4618 				ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
4619 				textViewer6.setHyperlinkDetectors(detectors, stateMask);
4620 			}
4621 			return;
4622 		}
4623 
4624 		if (PREFERENCE_HYPERLINK_KEY_MODIFIER.equals(property)) {
4625 			if (fSourceViewer instanceof ITextViewerExtension6) {
4626 				ITextViewerExtension6 textViewer6= (ITextViewerExtension6)fSourceViewer;
4627 				IHyperlinkDetector[] detectors= getSourceViewerConfiguration().getHyperlinkDetectors(fSourceViewer);
4628 				int stateMask= getSourceViewerConfiguration().getHyperlinkStateMask(fSourceViewer);
4629 				textViewer6.setHyperlinkDetectors(detectors, stateMask);
4630 			}
4631 			return;
4632 		}
4633 
4634 		if (PREFERENCE_RULER_CONTRIBUTIONS.equals(property)) {
4635 			String[] difference= StringSetSerializer.getDifference((String) event.getOldValue(), (String) event.getNewValue());
4636 			IColumnSupport support= getAdapter(IColumnSupport.class);
4637 			for (int i= 0; i < difference.length; i++) {
4638 				RulerColumnDescriptor desc= RulerColumnRegistry.getDefault().getColumnDescriptor(difference[i]);
4639 				if (desc != null &&  support.isColumnSupported(desc)) {
4640 					boolean newState= !support.isColumnVisible(desc);
4641 					support.setColumnVisible(desc, newState);
4642 				}
4643 			}
4644 			return;
4645 		}
4646 
4647 		if (PREFERENCE_SHOW_WHITESPACE_CHARACTERS.equals(property) ||
4648 				PREFERENCE_SHOW_LEADING_SPACES.equals(property) ||
4649 				PREFERENCE_SHOW_ENCLOSED_SPACES.equals(property) ||
4650 				PREFERENCE_SHOW_TRAILING_SPACES.equals(property) ||
4651 				PREFERENCE_SHOW_LEADING_IDEOGRAPHIC_SPACES.equals(property) ||
4652 				PREFERENCE_SHOW_ENCLOSED_IDEOGRAPHIC_SPACES.equals(property) ||
4653 				PREFERENCE_SHOW_TRAILING_IDEOGRAPHIC_SPACES.equals(property) ||
4654 				PREFERENCE_SHOW_LEADING_TABS.equals(property) ||
4655 				PREFERENCE_SHOW_ENCLOSED_TABS.equals(property) ||
4656 				PREFERENCE_SHOW_TRAILING_TABS.equals(property) ||
4657 				PREFERENCE_SHOW_CARRIAGE_RETURN.equals(property) ||
4658 				PREFERENCE_SHOW_LINE_FEED.equals(property) ||
4659 				PREFERENCE_WHITESPACE_CHARACTER_ALPHA_VALUE.equals(property)) {
4660 			IAction action= getAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS);
4661 			if (action instanceof IUpdate)
4662 				((IUpdate)action).update();
4663 			return;
4664 		}
4665 
4666 		if (PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED.equals(property)) {
4667 			IPreferenceStore store= getPreferenceStore();
4668 			if (store != null && store.getBoolean(PREFERENCE_TEXT_DRAG_AND_DROP_ENABLED))
4669 				installTextDragAndDrop(getSourceViewer());
4670 			else
4671 				uninstallTextDragAndDrop(getSourceViewer());
4672 			return;
4673 		}
4674 
4675 		if (PREFERENCE_HOVER_ENRICH_MODE.equals(property)) {
4676 			if (fSourceViewer instanceof ITextViewerExtension8) {
4677 				IPreferenceStore store= getPreferenceStore();
4678 				if (store != null) {
4679 					((ITextViewerExtension8)fSourceViewer).setHoverEnrichMode(convertEnrichModePreference(store.getInt(PREFERENCE_HOVER_ENRICH_MODE)));
4680 				}
4681 			}
4682 			return;
4683 		}
4684 
4685 	}
4686 
4687 	/**
4688 	 * Returns the progress monitor related to this editor. It should not be
4689 	 * necessary to extend this method.
4690 	 *
4691 	 * @return the progress monitor related to this editor
4692 	 * @since 2.1
4693 	 */
getProgressMonitor()4694 	protected IProgressMonitor getProgressMonitor() {
4695 
4696 		IProgressMonitor pm= null;
4697 
4698 		IStatusLineManager manager= getStatusLineManager();
4699 		if (manager != null)
4700 			pm= manager.getProgressMonitor();
4701 
4702 		return pm != null ? pm : new NullProgressMonitor();
4703 	}
4704 
4705 	/**
4706 	 * Handles an external change of the editor's input element. Subclasses may
4707 	 * extend.
4708 	 */
handleEditorInputChanged()4709 	protected void handleEditorInputChanged() {
4710 
4711 		String title;
4712 		String msg;
4713 		Shell shell= getSite().getShell();
4714 
4715 		final IDocumentProvider provider= getDocumentProvider();
4716 		if (provider == null) {
4717 			// fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=15066
4718 			close(false);
4719 			return;
4720 		}
4721 
4722 		final IEditorInput input= getEditorInput();
4723 		final String inputName= input.getToolTipText();
4724 
4725 		if (provider.isDeleted(input)) {
4726 
4727 			if (isSaveAsAllowed()) {
4728 
4729 				title= EditorMessages.Editor_error_activated_deleted_save_title;
4730 				msg= NLSUtility.format(EditorMessages.Editor_error_activated_deleted_save_message, inputName);
4731 
4732 				String[] buttons= {
4733 						EditorMessages.Editor_error_activated_deleted_save_button_save,
4734 						EditorMessages.Editor_error_activated_deleted_save_button_close,
4735 				};
4736 
4737 				MessageDialog dialog= new MessageDialog(shell, title, null, msg, MessageDialog.QUESTION, buttons, 0);
4738 
4739 				if (dialog.open() == 0) {
4740 					IProgressMonitor pm= getProgressMonitor();
4741 					try {
4742 						performSaveAs(pm);
4743 						if (pm.isCanceled())
4744 							handleEditorInputChanged();
4745 					} finally {
4746 						pm.done();
4747 					}
4748 				} else {
4749 					close(false);
4750 				}
4751 
4752 			} else {
4753 
4754 				title= EditorMessages.Editor_error_activated_deleted_close_title;
4755 				msg= NLSUtility.format(EditorMessages.Editor_error_activated_deleted_close_message, inputName);
4756 				if (MessageDialog.openConfirm(shell, title, msg))
4757 					close(false);
4758 			}
4759 
4760 		} else if (fHasBeenActivated) {
4761 
4762 			title= EditorMessages.Editor_error_activated_outofsync_title;
4763 			msg= NLSUtility.format(EditorMessages.Editor_error_activated_outofsync_message, inputName);
4764 
4765 			if (MessageDialog.open(MessageDialog.QUESTION, shell, title, msg, SWT.NONE,
4766 					EditorMessages.Editor_error_replace_button_label,
4767 					EditorMessages.Editor_error_dontreplace_button_label) == 0) {
4768 
4769 				try {
4770 					if (provider instanceof IDocumentProviderExtension) {
4771 						IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
4772 						extension.synchronize(input);
4773 					} else {
4774 						doSetInput(input);
4775 					}
4776 				} catch (CoreException x) {
4777 					IStatus status= x.getStatus();
4778 					if (status == null || status.getSeverity() != IStatus.CANCEL) {
4779 						title= EditorMessages.Editor_error_refresh_outofsync_title;
4780 						msg= NLSUtility.format(EditorMessages.Editor_error_refresh_outofsync_message, inputName);
4781 						ErrorDialog.openError(shell, title, msg, x.getStatus());
4782 					}
4783 				}
4784 			} else if (!isDirty()) {
4785 				// Trigger dummy change to dirty the editor, for details see https://bugs.eclipse.org/344101 .
4786 				try {
4787 					IDocument document= provider.getDocument(input);
4788 					if (document != null)
4789 						document.replace(0, 0, ""); //$NON-NLS-1$
4790 				} catch (BadLocationException e) {
4791 					// Ignore as this can't happen
4792 				}
4793 			}
4794 		}
4795 	}
4796 
4797 	/**
4798 	 * The <code>AbstractTextEditor</code> implementation of this
4799 	 * <code>IEditorPart</code> method calls <code>performSaveAs</code>.
4800 	 * Subclasses may reimplement.
4801 	 */
4802 	@Override
doSaveAs()4803 	public void doSaveAs() {
4804 		IProgressMonitor monitor= getProgressMonitor();
4805 		try {
4806 			/*
4807 			 * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
4808 			 * Changed Behavior to make sure that if called inside a regular save (because
4809 			 * of deletion of input element) there is a way to report back to the caller.
4810 			 */
4811 			performSaveAs(monitor);
4812 		} finally {
4813 			monitor.done();
4814 		}
4815 	}
4816 
4817 	/**
4818 	 * Performs a save as and reports the result state back to the
4819 	 * given progress monitor. This default implementation does nothing.
4820 	 * Subclasses may reimplement.
4821 	 *
4822 	 * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
4823 	 */
performSaveAs(IProgressMonitor progressMonitor)4824 	protected void performSaveAs(IProgressMonitor progressMonitor) {
4825 	}
4826 
4827 	/**
4828 	 * The <code>AbstractTextEditor</code> implementation of this
4829 	 * <code>IEditorPart</code> method may be extended by subclasses.
4830 	 *
4831 	 * @param progressMonitor the progress monitor for communicating result state or <code>null</code>
4832 	 */
4833 	@Override
doSave(IProgressMonitor progressMonitor)4834 	public void doSave(IProgressMonitor progressMonitor) {
4835 
4836 		IDocumentProvider p= getDocumentProvider();
4837 		if (p == null)
4838 			return;
4839 
4840 		if (p.isDeleted(getEditorInput())) {
4841 
4842 			if (isSaveAsAllowed()) {
4843 
4844 				/*
4845 				 * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors.
4846 				 * Changed Behavior to make sure that if called inside a regular save (because
4847 				 * of deletion of input element) there is a way to report back to the caller.
4848 				 */
4849 				performSaveAs(progressMonitor);
4850 
4851 			} else {
4852 
4853 				Shell shell= getSite().getShell();
4854 				String title= EditorMessages.Editor_error_save_deleted_title;
4855 				String msg= EditorMessages.Editor_error_save_deleted_message;
4856 				MessageDialog.openError(shell, title, msg);
4857 			}
4858 
4859 		} else {
4860 			updateState(getEditorInput());
4861 			validateState(getEditorInput());
4862 			performSave(false, progressMonitor);
4863 		}
4864 	}
4865 
4866 	/**
4867 	 * Enables/disables sanity checking.
4868 	 * @param enable <code>true</code> if sanity checking should be enabled, <code>false</code> otherwise
4869 	 * @since 2.0
4870 	 */
enableSanityChecking(boolean enable)4871 	protected void enableSanityChecking(boolean enable) {
4872 		synchronized (this) {
4873 			fIsSanityCheckEnabled= enable;
4874 		}
4875 	}
4876 
4877 	/**
4878 	 * Checks the state of the given editor input if sanity checking is enabled.
4879 	 * @param input the editor input whose state is to be checked
4880 	 * @since 2.0
4881 	 */
safelySanityCheckState(IEditorInput input)4882 	protected void safelySanityCheckState(IEditorInput input) {
4883 		boolean enabled= false;
4884 
4885 		synchronized (this) {
4886 			enabled= fIsSanityCheckEnabled;
4887 		}
4888 
4889 		if (enabled)
4890 			sanityCheckState(input);
4891 	}
4892 
4893 	/**
4894 	 * Checks the state of the given editor input.
4895 	 * @param input the editor input whose state is to be checked
4896 	 * @since 2.0
4897 	 */
sanityCheckState(IEditorInput input)4898 	protected void sanityCheckState(IEditorInput input) {
4899 
4900 		IDocumentProvider p= getDocumentProvider();
4901 		if (p == null)
4902 			return;
4903 
4904 		if (p instanceof IDocumentProviderExtension3)  {
4905 
4906 			IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;
4907 
4908 			long stamp= p.getModificationStamp(input);
4909 			if (stamp != fModificationStamp) {
4910 				fModificationStamp= stamp;
4911 				if (!p3.isSynchronized(input))
4912 					handleEditorInputChanged();
4913 			}
4914 
4915 		} else  {
4916 
4917 			if (fModificationStamp == -1)
4918 				fModificationStamp= p.getSynchronizationStamp(input);
4919 
4920 			long stamp= p.getModificationStamp(input);
4921 			if (stamp != fModificationStamp) {
4922 				fModificationStamp= stamp;
4923 				if (stamp != p.getSynchronizationStamp(input))
4924 					handleEditorInputChanged();
4925 			}
4926 		}
4927 
4928 		updateState(getEditorInput());
4929 		updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE);
4930 	}
4931 
4932 	/**
4933 	 * Enables/disables state validation.
4934 	 * @param enable <code>true</code> if state validation should be enabled, <code>false</code> otherwise
4935 	 * @since 2.1
4936 	 */
enableStateValidation(boolean enable)4937 	protected void enableStateValidation(boolean enable) {
4938 		synchronized (this) {
4939 			fIsStateValidationEnabled= enable;
4940 		}
4941 	}
4942 
4943 	/**
4944 	 * Validates the state of the given editor input. The predominate intent
4945 	 * of this method is to take any action probably necessary to ensure that
4946 	 * the input can persistently be changed.
4947 	 *
4948 	 * @param input the input to be validated
4949 	 * @since 2.0
4950 	 */
validateState(IEditorInput input)4951 	protected void validateState(IEditorInput input) {
4952 
4953 		IDocumentProvider provider= getDocumentProvider();
4954 		if (! (provider instanceof IDocumentProviderExtension))
4955 			return;
4956 
4957 		IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
4958 
4959 		try {
4960 
4961 			extension.validateState(input, getSite().getShell());
4962 
4963 		} catch (CoreException x) {
4964 			IStatus status= x.getStatus();
4965 			if (status == null || status.getSeverity() != IStatus.CANCEL) {
4966 				Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
4967 				ILog log= Platform.getLog(bundle);
4968 				log.log(x.getStatus());
4969 
4970 				Shell shell= getSite().getShell();
4971 				String title= EditorMessages.Editor_error_validateEdit_title;
4972 				String msg= EditorMessages.Editor_error_validateEdit_message;
4973 				ErrorDialog.openError(shell, title, msg, x.getStatus());
4974 			}
4975 			return;
4976 		}
4977 
4978 		if (fSourceViewer != null)
4979 			fSourceViewer.setEditable(isEditable());
4980 
4981 		updateStateDependentActions();
4982 	}
4983 
4984 	@Override
validateEditorInputState()4985 	public boolean validateEditorInputState() {
4986 
4987 		boolean enabled= false;
4988 
4989 		synchronized (this) {
4990 			enabled= fIsStateValidationEnabled;
4991 		}
4992 
4993 		if (enabled) {
4994 
4995 			ISourceViewer viewer= fSourceViewer;
4996 			if (viewer == null)
4997 				return false;
4998 
4999 			fTextInputListener.inputChanged= false;
5000 			viewer.addTextInputListener(fTextInputListener);
5001 
5002 			try {
5003 				final IEditorInput input= getEditorInput();
5004 				BusyIndicator.showWhile(getSite().getShell().getDisplay(), () -> validateState(input));
5005 				sanityCheckState(input);
5006 				return !isEditorInputReadOnly() && !fTextInputListener.inputChanged;
5007 
5008 			} finally {
5009 				viewer.removeTextInputListener(fTextInputListener);
5010 			}
5011 
5012 		}
5013 
5014 		return !isEditorInputReadOnly();
5015 	}
5016 
5017 	/**
5018 	 * Updates the state of the given editor input such as read-only flag.
5019 	 *
5020 	 * @param input the input to be validated
5021 	 * @since 2.0
5022 	 */
updateState(IEditorInput input)5023 	protected void updateState(IEditorInput input) {
5024 		IDocumentProvider provider= getDocumentProvider();
5025 		if (provider instanceof IDocumentProviderExtension) {
5026 			IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
5027 			try {
5028 
5029 				boolean wasReadOnly= isEditorInputReadOnly();
5030 				extension.updateStateCache(input);
5031 
5032 				if (fSourceViewer != null)
5033 					fSourceViewer.setEditable(isEditable());
5034 
5035 				if (wasReadOnly != isEditorInputReadOnly())
5036 					updateStateDependentActions();
5037 
5038 			} catch (CoreException x) {
5039 				Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID);
5040 				ILog log= Platform.getLog(bundle);
5041 				log.log(x.getStatus());
5042 			}
5043 		}
5044 	}
5045 
5046 	/**
5047 	 * Performs the save and handles errors appropriately.
5048 	 *
5049 	 * @param overwrite indicates whether or not overwriting is allowed
5050 	 * @param progressMonitor the monitor in which to run the operation
5051 	 * @since 3.0
5052 	 */
performSave(boolean overwrite, IProgressMonitor progressMonitor)5053 	protected void performSave(boolean overwrite, IProgressMonitor progressMonitor) {
5054 		IDocumentProvider provider= getDocumentProvider();
5055 		if (provider == null)
5056 			return;
5057 
5058 		fHandleActivation= false;
5059 		try {
5060 			provider.aboutToChange(getEditorInput());
5061 			IEditorInput input= getEditorInput();
5062 			provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), overwrite);
5063 			editorSaved();
5064 
5065 		} catch (CoreException x) {
5066 			IStatus status= x.getStatus();
5067 			if (status == null || status.getSeverity() != IStatus.CANCEL)
5068 				handleExceptionOnSave(x, progressMonitor);
5069 		} finally {
5070 			provider.changed(getEditorInput());
5071 			fHandleActivation= true;
5072 		}
5073 	}
5074 
5075 	/**
5076 	 * Handles the given exception. If the exception reports an out-of-sync
5077 	 * situation, this is reported to the user. Otherwise, the exception
5078 	 * is generically reported.
5079 	 *
5080 	 * @param exception the exception to handle
5081 	 * @param progressMonitor the progress monitor
5082 	 */
handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor)5083 	protected void handleExceptionOnSave(CoreException exception, IProgressMonitor progressMonitor) {
5084 
5085 		try {
5086 			++fErrorCorrectionOnSave;
5087 
5088 			boolean isSynchronized= false;
5089 			IDocumentProvider p= getDocumentProvider();
5090 
5091 			if (p instanceof IDocumentProviderExtension3)  {
5092 				IDocumentProviderExtension3 p3= (IDocumentProviderExtension3) p;
5093 				isSynchronized= p3.isSynchronized(getEditorInput());
5094 			} else if (p != null) {
5095 				long modifiedStamp= p.getModificationStamp(getEditorInput());
5096 				long synchStamp= p.getSynchronizationStamp(getEditorInput());
5097 				isSynchronized= (modifiedStamp == synchStamp);
5098 			}
5099 
5100 			if (isNotSynchronizedException(exception) && fErrorCorrectionOnSave == 1 && !isSynchronized) {
5101 				String title= EditorMessages.Editor_error_save_outofsync_title;
5102 				String msg= NLSUtility.format(EditorMessages.Editor_error_save_outofsync_message, getEditorInput().getToolTipText());
5103 
5104 				if (MessageDialog.openQuestion(getSite().getShell(), title, msg))
5105 					performSave(true, progressMonitor);
5106 				else {
5107 					/*
5108 					 * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
5109 					 * Set progress monitor to canceled in order to report back
5110 					 * to enclosing operations.
5111 					 */
5112 					if (progressMonitor != null)
5113 						progressMonitor.setCanceled(true);
5114 				}
5115 			} else {
5116 				String title= EditorMessages.Editor_error_save_title;
5117 				String msg= EditorMessages.Editor_error_save_message;
5118 				openSaveErrorDialog(title, msg, exception);
5119 
5120 				/*
5121 				 * 1GEUPKR: ITPJUI:ALL - Loosing work with simultaneous edits
5122 				 * Set progress monitor to canceled in order to report back
5123 				 * to enclosing operations.
5124 				 */
5125 				if (progressMonitor != null)
5126 					progressMonitor.setCanceled(true);
5127 			}
5128 		} finally {
5129 			--fErrorCorrectionOnSave;
5130 		}
5131 	}
5132 
5133 	/**
5134 	 * Presents an error dialog to the user when a problem
5135 	 * happens during save.
5136 	 * <p>
5137 	 * Subclasses can decide to override the given title and message.
5138 	 * </p>
5139 	 *
5140 	 * @param title	the dialog title
5141 	 * @param message the message to display
5142 	 * @param exception the exception to handle
5143 	 * @since 3.3
5144 	 */
openSaveErrorDialog(String title, String message, CoreException exception)5145 	protected void openSaveErrorDialog(String title, String message, CoreException  exception) {
5146 		ErrorDialog.openError(getSite().getShell(), title, message, exception.getStatus());
5147 	}
5148 
5149 	/**
5150 	 * Tells whether the given core exception is exactly the
5151 	 * exception which is thrown for a non-synchronized element.
5152 	 *
5153 	 * @param ex the core exception
5154 	 * @return <code>true</code> iff the given core exception is exactly the
5155 	 *			exception which is thrown for a non-synchronized element
5156 	 * @since 3.1
5157 	 */
isNotSynchronizedException(CoreException ex)5158 	private boolean isNotSynchronizedException(CoreException ex) {
5159 		IDocumentProvider provider= getDocumentProvider();
5160 		if (provider instanceof IDocumentProviderExtension5)
5161 			return ((IDocumentProviderExtension5)provider).isNotSynchronizedException(getEditorInput(), ex);
5162 		return false;
5163 	}
5164 
5165 	/**
5166 	 * The <code>AbstractTextEditor</code> implementation of this
5167 	 * <code>IEditorPart</code> method returns <code>false</code>.
5168 	 * Subclasses may override.
5169 	 *
5170 	 * @return <code>false</code>
5171 	 */
5172 	@Override
isSaveAsAllowed()5173 	public boolean isSaveAsAllowed() {
5174 		return false;
5175 	}
5176 
5177 	@Override
isDirty()5178 	public boolean isDirty() {
5179 		IDocumentProvider p= getDocumentProvider();
5180 		return p == null ? false : p.canSaveDocument(getEditorInput());
5181 	}
5182 
5183 	/**
5184 	 * The <code>AbstractTextEditor</code> implementation of this
5185 	 * <code>ITextEditor</code> method may be extended by subclasses.
5186 	 */
5187 	@Override
doRevertToSaved()5188 	public void doRevertToSaved() {
5189 		IDocumentProvider p= getDocumentProvider();
5190 		if (p == null)
5191 			return;
5192 
5193 		performRevert();
5194 	}
5195 
5196 	/**
5197 	 * Performs revert and handles errors appropriately.
5198 	 * <p>
5199 	 * Subclasses may extend.
5200 	 * </p>
5201 	 *
5202 	 * @since 3.0
5203 	 */
performRevert()5204 	protected void performRevert() {
5205 
5206 		IDocumentProvider provider= getDocumentProvider();
5207 		if (provider == null)
5208 			return;
5209 
5210 		try {
5211 
5212 			provider.aboutToChange(getEditorInput());
5213 			provider.resetDocument(getEditorInput());
5214 			editorSaved();
5215 
5216 		} catch (CoreException x) {
5217 			IStatus status= x.getStatus();
5218 			if (status == null || status.getSeverity() != IStatus.CANCEL ) {
5219 				Shell shell= getSite().getShell();
5220 				String title= EditorMessages.Editor_error_revert_title;
5221 				String msg= EditorMessages.Editor_error_revert_message;
5222 				ErrorDialog.openError(shell, title, msg, x.getStatus());
5223 			}
5224 		} finally {
5225 			provider.changed(getEditorInput());
5226 		}
5227 	}
5228 
5229 	/**
5230 	 * Performs any additional action necessary to perform after the input
5231 	 * document's content has been replaced.
5232 	 * <p>
5233 	 * Clients may extended this method.
5234 	 *
5235 	 * @since 3.0
5236 	 */
handleElementContentReplaced()5237 	protected void handleElementContentReplaced() {
5238 	}
5239 
5240 	@Override
setAction(String actionID, IAction action)5241 	public void setAction(String actionID, IAction action) {
5242 		Assert.isNotNull(actionID);
5243 		if (action == null) {
5244 			action= fActions.remove(actionID);
5245 			if (action != null)
5246 				fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
5247 		} else {
5248 			if (action.getId() == null)
5249 				action.setId(actionID); // make sure the action ID has been set
5250 			fActions.put(actionID, action);
5251 			fActivationCodeTrigger.registerActionForKeyActivation(action);
5252 		}
5253 	}
5254 
5255 	/**
5256 	 * Sets this editor's actions into activated (default) or deactived state.
5257 	 * <p>
5258 	 * XXX: This is called by the Java editor for its breadcrumb feature. We
5259 	 * don't want to make this risky method API because the Java editor
5260 	 * breadcrumb might become a Platform UI feature during 3.5 and hence we can
5261 	 * then delete this workaround.
5262 	 * </p>
5263 	 *
5264 	 * @param state <code>true</code> if activated
5265 	 * @since 3.4
5266 	 */
setActionActivation(boolean state)5267 	private void setActionActivation(boolean state) {
5268 		if (state) {
5269 			fActivationCodeTrigger.install();
5270 			Iterator<IAction> iter= fActions.values().iterator();
5271 			while (iter.hasNext()) {
5272 				IAction action= iter.next();
5273 				if (action != null)
5274 					fActivationCodeTrigger.registerActionForKeyActivation(action);
5275 			}
5276 			getEditorSite().getActionBarContributor().setActiveEditor(this);
5277 		} else {
5278 			getEditorSite().getActionBarContributor().setActiveEditor(null);
5279 			Iterator<IAction> iter= fActions.values().iterator();
5280 			while (iter.hasNext()) {
5281 				IAction action= iter.next();
5282 				if (action != null)
5283 					fActivationCodeTrigger.unregisterActionFromKeyActivation(action);
5284 			}
5285 			fActivationCodeTrigger.uninstall();
5286 		}
5287 	}
5288 
5289 	private static final boolean HACK_TO_SUPPRESS_UNUSUED_WARNING= false;
5290 	{
5291 		if (HACK_TO_SUPPRESS_UNUSUED_WARNING)
5292 			setActionActivation(true);
5293 	}
5294 
5295 	@Override
setActionActivationCode(String actionID, char activationCharacter, int activationKeyCode, int activationStateMask)5296 	public void setActionActivationCode(String actionID, char activationCharacter, int activationKeyCode, int activationStateMask) {
5297 
5298 		Assert.isNotNull(actionID);
5299 
5300 		ActionActivationCode found= findActionActivationCode(actionID);
5301 		if (found == null) {
5302 			found= new ActionActivationCode(actionID);
5303 			fActivationCodes.add(found);
5304 		}
5305 
5306 		found.fCharacter= activationCharacter;
5307 		found.fKeyCode= activationKeyCode;
5308 		found.fStateMask= activationStateMask;
5309 	}
5310 
5311 	/**
5312 	 * Returns the activation code registered for the specified action.
5313 	 *
5314 	 * @param actionID the action id
5315 	 * @return the registered activation code or <code>null</code> if no code has been installed
5316 	 */
findActionActivationCode(String actionID)5317 	private ActionActivationCode findActionActivationCode(String actionID) {
5318 		int size= fActivationCodes.size();
5319 		for (int i= 0; i < size; i++) {
5320 			ActionActivationCode code= fActivationCodes.get(i);
5321 			if (actionID.equals(code.fActionId))
5322 				return code;
5323 		}
5324 		return null;
5325 	}
5326 
5327 	@Override
removeActionActivationCode(String actionID)5328 	public void removeActionActivationCode(String actionID) {
5329 		Assert.isNotNull(actionID);
5330 		ActionActivationCode code= findActionActivationCode(actionID);
5331 		if (code != null)
5332 			fActivationCodes.remove(code);
5333 	}
5334 
5335 	@Override
getAction(String actionID)5336 	public IAction getAction(String actionID) {
5337 		Assert.isNotNull(actionID);
5338 		IAction action= fActions.get(actionID);
5339 
5340 		if (action == null) {
5341 			action= findContributedAction(actionID);
5342 			if (action != null)
5343 				setAction(actionID, action);
5344 		}
5345 
5346 		return action;
5347 	}
5348 
5349 	/**
5350 	 * Returns the action with the given action id that has been contributed via XML to this editor.
5351 	 * The lookup honors the dependencies of plug-ins.
5352 	 *
5353 	 * @param actionID the action id to look up
5354 	 * @return the action that has been contributed
5355 	 * @since 2.0
5356 	 */
findContributedAction(String actionID)5357 	private IAction findContributedAction(String actionID) {
5358 		List<IConfigurationElement> actions= new ArrayList<>();
5359 		IConfigurationElement[] elements= Platform.getExtensionRegistry().getConfigurationElementsFor(PlatformUI.PLUGIN_ID, "editorActions"); //$NON-NLS-1$
5360 		for (int i= 0; i < elements.length; i++) {
5361 			IConfigurationElement element= elements[i];
5362 			if (TAG_CONTRIBUTION_TYPE.equals(element.getName())) {
5363 				IWorkbenchPartSite site = getSite();
5364 				if (site == null) {
5365 					return null;
5366 				}
5367 				if (!site.getId().equals(element.getAttribute("targetID"))) //$NON-NLS-1$
5368 					continue;
5369 
5370 				IConfigurationElement[] children= element.getChildren("action"); //$NON-NLS-1$
5371 				for (int j= 0; j < children.length; j++) {
5372 					IConfigurationElement child= children[j];
5373 					if (actionID.equals(child.getAttribute("actionID"))) //$NON-NLS-1$
5374 						actions.add(child);
5375 				}
5376 			}
5377 		}
5378 		int actionSize= actions.size();
5379 		if (actionSize > 0) {
5380 			IConfigurationElement element;
5381 			if (actionSize > 1) {
5382 				IConfigurationElement[] actionArray= actions.toArray(new IConfigurationElement[actionSize]);
5383 				ConfigurationElementSorter sorter= new ConfigurationElementSorter() {
5384 					@Override
5385 					public IConfigurationElement getConfigurationElement(Object object) {
5386 						return (IConfigurationElement)object;
5387 					}
5388 				};
5389 				sorter.sort(actionArray);
5390 				element= actionArray[0];
5391 			} else
5392 				element= actions.get(0);
5393 
5394 			try {
5395 				return new ContributedAction(getSite(), element);
5396 			} catch (CommandNotMappedException e) {
5397 				// out of luck, no command action mapping
5398 			}
5399 		}
5400 
5401 		return null;
5402 	}
5403 
5404 	/**
5405 	 * Updates the specified action by calling <code>IUpdate.update</code>
5406 	 * if applicable.
5407 	 *
5408 	 * @param actionId the action id
5409 	 */
updateAction(String actionId)5410 	private void updateAction(String actionId) {
5411 		Assert.isNotNull(actionId);
5412 		if (fActions != null) {
5413 			IAction action= fActions.get(actionId);
5414 			if (action instanceof IUpdate)
5415 				((IUpdate) action).update();
5416 		}
5417 	}
5418 
5419 	/**
5420 	 * Marks or unmarks the given action to be updated on text selection changes.
5421 	 *
5422 	 * @param actionId the action id
5423 	 * @param mark <code>true</code> if the action is selection dependent
5424 	 */
markAsSelectionDependentAction(String actionId, boolean mark)5425 	public void markAsSelectionDependentAction(String actionId, boolean mark) {
5426 		Assert.isNotNull(actionId);
5427 		if (mark) {
5428 			if (!fSelectionActions.contains(actionId))
5429 				fSelectionActions.add(actionId);
5430 		} else
5431 			fSelectionActions.remove(actionId);
5432 	}
5433 
5434 	/**
5435 	 * Marks or unmarks the given action to be updated on content changes.
5436 	 *
5437 	 * @param actionId the action id
5438 	 * @param mark <code>true</code> if the action is content dependent
5439 	 */
markAsContentDependentAction(String actionId, boolean mark)5440 	public void markAsContentDependentAction(String actionId, boolean mark) {
5441 		Assert.isNotNull(actionId);
5442 		if (mark) {
5443 			if (!fContentActions.contains(actionId))
5444 				fContentActions.add(actionId);
5445 		} else
5446 			fContentActions.remove(actionId);
5447 	}
5448 
5449 	/**
5450 	 * Marks or unmarks the given action to be updated on property changes.
5451 	 *
5452 	 * @param actionId the action id
5453 	 * @param mark <code>true</code> if the action is property dependent
5454 	 * @since 2.0
5455 	 */
markAsPropertyDependentAction(String actionId, boolean mark)5456 	public void markAsPropertyDependentAction(String actionId, boolean mark) {
5457 		Assert.isNotNull(actionId);
5458 		if (mark) {
5459 			if (!fPropertyActions.contains(actionId))
5460 				fPropertyActions.add(actionId);
5461 		} else
5462 			fPropertyActions.remove(actionId);
5463 	}
5464 
5465 	/**
5466 	 * Marks or unmarks the given action to be updated on state changes.
5467 	 *
5468 	 * @param actionId the action id
5469 	 * @param mark <code>true</code> if the action is state dependent
5470 	 * @since 2.0
5471 	 */
markAsStateDependentAction(String actionId, boolean mark)5472 	public void markAsStateDependentAction(String actionId, boolean mark) {
5473 		Assert.isNotNull(actionId);
5474 		if (mark) {
5475 			if (!fStateActions.contains(actionId))
5476 				fStateActions.add(actionId);
5477 		} else
5478 			fStateActions.remove(actionId);
5479 	}
5480 
5481 	/**
5482 	 * Updates all selection dependent actions.
5483 	 */
updateSelectionDependentActions()5484 	protected void updateSelectionDependentActions() {
5485 		if (fSelectionActions != null) {
5486 			Iterator<String> e= fSelectionActions.iterator();
5487 			while (e.hasNext())
5488 				updateAction(e.next());
5489 		}
5490 	}
5491 
5492 	/**
5493 	 * Updates all content dependent actions.
5494 	 */
updateContentDependentActions()5495 	protected void updateContentDependentActions() {
5496 		if (fContentActions != null) {
5497 			Iterator<String> e= fContentActions.iterator();
5498 			while (e.hasNext())
5499 				updateAction(e.next());
5500 		}
5501 	}
5502 
5503 	/**
5504 	 * Updates all property dependent actions.
5505 	 * @since 2.0
5506 	 */
updatePropertyDependentActions()5507 	protected void updatePropertyDependentActions() {
5508 		if (fPropertyActions != null) {
5509 			Iterator<String> e= fPropertyActions.iterator();
5510 			while (e.hasNext())
5511 				updateAction(e.next());
5512 		}
5513 	}
5514 
5515 	/**
5516 	 * Updates all state dependent actions.
5517 	 * @since 2.0
5518 	 */
updateStateDependentActions()5519 	protected void updateStateDependentActions() {
5520 		if (fStateActions != null) {
5521 			Iterator<String> e= fStateActions.iterator();
5522 			while (e.hasNext())
5523 				updateAction(e.next());
5524 		}
5525 	}
5526 
5527 	/**
5528 	 * Creates action entries for all SWT StyledText actions as defined in
5529 	 * <code>org.eclipse.swt.custom.ST</code>. Overwrites and
5530 	 * extends the list of these actions afterwards.
5531 	 * <p>
5532 	 * Subclasses may extend.
5533 	 * </p>
5534 	 * @since 2.0
5535 	 */
createNavigationActions()5536 	protected void createNavigationActions() {
5537 
5538 		IAction action;
5539 
5540 		StyledText textWidget= fSourceViewer.getTextWidget();
5541 		for (int i= 0; i < ACTION_MAP.length; i++) {
5542 			IdMapEntry entry= ACTION_MAP[i];
5543 			action= new TextNavigationAction(textWidget, entry.getAction());
5544 			action.setActionDefinitionId(entry.getActionId());
5545 			setAction(entry.getActionId(), action);
5546 		}
5547 
5548 		action= new ToggleOverwriteModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleOverwriteMode."); //$NON-NLS-1$
5549 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE);
5550 		setAction(ITextEditorActionDefinitionIds.TOGGLE_OVERWRITE, action);
5551 		textWidget.setKeyBinding(SWT.INSERT, SWT.NULL);
5552 
5553 		action=  new ScrollLinesAction(-1);
5554 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_UP);
5555 		setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_UP, action);
5556 
5557 		action= new ScrollLinesAction(1);
5558 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN);
5559 		setAction(ITextEditorActionDefinitionIds.SCROLL_LINE_DOWN, action);
5560 
5561 		action= new LineEndAction(textWidget, false);
5562 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_END);
5563 		setAction(ITextEditorActionDefinitionIds.LINE_END, action);
5564 
5565 		action= new LineStartAction(textWidget, false);
5566 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_START);
5567 		setAction(ITextEditorActionDefinitionIds.LINE_START, action);
5568 
5569 		action= new LineEndAction(textWidget, true);
5570 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_END);
5571 		setAction(ITextEditorActionDefinitionIds.SELECT_LINE_END, action);
5572 
5573 		action= new LineStartAction(textWidget, true);
5574 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SELECT_LINE_START);
5575 		setAction(ITextEditorActionDefinitionIds.SELECT_LINE_START, action);
5576 
5577 		// to accommodate https://bugs.eclipse.org/bugs/show_bug.cgi?id=51516
5578 		// nullify handling of DELETE key by StyledText
5579 		textWidget.setKeyBinding(SWT.DEL, SWT.NULL);
5580 	}
5581 
5582 	/**
5583 	 * Creates this editor's accessibility actions.
5584 	 * @since 2.0
5585 	 */
createAccessibilityActions()5586 	private void createAccessibilityActions() {
5587 		IAction action= new ShowRulerContextMenuAction();
5588 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU);
5589 		setAction(ITextEditorActionDefinitionIds.SHOW_RULER_CONTEXT_MENU, action);
5590 	}
5591 
5592 	/**
5593 	 * Creates this editor's undo/redo actions.
5594 	 * <p>
5595 	 * Subclasses may override or extend.</p>
5596 	 *
5597 	 * @since 3.1
5598 	 */
createUndoRedoActions()5599 	protected void createUndoRedoActions() {
5600 		IUndoContext undoContext= getUndoContext();
5601 		if (undoContext != null) {
5602 			// Use actions provided by global undo/redo
5603 
5604 			// Create the undo action
5605 			OperationHistoryActionHandler undoAction= new UndoActionHandler(getEditorSite(), undoContext);
5606 			PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION);
5607 			undoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
5608 			registerUndoRedoAction(ITextEditorActionConstants.UNDO, undoAction);
5609 
5610 			// Create the redo action.
5611 			OperationHistoryActionHandler redoAction= new RedoActionHandler(getEditorSite(), undoContext);
5612 			PlatformUI.getWorkbench().getHelpSystem().setHelp(redoAction, IAbstractTextEditorHelpContextIds.REDO_ACTION);
5613 			redoAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
5614 			registerUndoRedoAction(ITextEditorActionConstants.REDO, redoAction);
5615 
5616 			// Install operation approvers
5617 			IOperationHistory history= OperationHistoryFactory.getOperationHistory();
5618 
5619 			// The first approver will prompt when operations affecting outside elements are to be undone or redone.
5620 			if (fNonLocalOperationApprover != null)
5621 				history.removeOperationApprover(fNonLocalOperationApprover);
5622 			fNonLocalOperationApprover= getUndoRedoOperationApprover(undoContext);
5623 			history.addOperationApprover(fNonLocalOperationApprover);
5624 
5625 			// The second approver will prompt from this editor when an undo is attempted on an operation
5626 			// and it is not the most recent operation in the editor.
5627 			if (fLinearUndoViolationApprover != null)
5628 				history.removeOperationApprover(fLinearUndoViolationApprover);
5629 			fLinearUndoViolationApprover= new LinearUndoViolationUserApprover(undoContext, this);
5630 			history.addOperationApprover(fLinearUndoViolationApprover);
5631 
5632 		} else {
5633 			// Use text operation actions (pre 3.1 style)
5634 
5635 			ResourceAction action;
5636 
5637 			if (getAction(ITextEditorActionConstants.UNDO) == null) {
5638 				action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Undo.", this, ITextOperationTarget.UNDO); //$NON-NLS-1$
5639 				action.setHelpContextId(IAbstractTextEditorHelpContextIds.UNDO_ACTION);
5640 				action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_UNDO);
5641 				setAction(ITextEditorActionConstants.UNDO, action);
5642 			}
5643 
5644 			if (getAction(ITextEditorActionConstants.REDO) == null) {
5645 				action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Redo.", this, ITextOperationTarget.REDO); //$NON-NLS-1$
5646 				action.setHelpContextId(IAbstractTextEditorHelpContextIds.REDO_ACTION);
5647 				action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_REDO);
5648 				setAction(ITextEditorActionConstants.REDO, action);
5649 			}
5650 		}
5651 	}
5652 
5653 	/**
5654 	 * Registers the given undo/redo action under the given ID and ensures that previously installed
5655 	 * actions get disposed. It also takes care of re-registering the new action with the global
5656 	 * action handler.
5657 	 *
5658 	 * @param actionId the action id under which to register the action
5659 	 * @param action the action to register or <code>null</code> to dispose them
5660 	 * @since 3.1
5661 	 */
registerUndoRedoAction(String actionId, OperationHistoryActionHandler action)5662 	private void registerUndoRedoAction(String actionId, OperationHistoryActionHandler action) {
5663 		IAction oldAction= getAction(actionId);
5664 		if (oldAction instanceof OperationHistoryActionHandler)
5665 			((OperationHistoryActionHandler)oldAction).dispose();
5666 
5667 		if (action == null)
5668 			return;
5669 
5670 		setAction(actionId, action);
5671 
5672 		IActionBars actionBars= getEditorSite().getActionBars();
5673 		if (actionBars != null)
5674 			actionBars.setGlobalActionHandler(actionId, action);
5675 	}
5676 
5677 	/**
5678 	 * Return an {@link IOperationApprover} appropriate for approving the undo and
5679 	 * redo of operations that have the specified undo context.
5680 	 * <p>
5681 	 * Subclasses may override.
5682 	 * </p>
5683 	 * @param undoContext	the IUndoContext of operations that should be examined
5684 	 * 						by the operation approver
5685 	 * @return	the <code>IOperationApprover</code> appropriate for approving undo
5686 	 * 			and redo operations inside this editor, or <code>null</code> if no
5687 	 * 			approval is needed
5688 	 * @since 3.1
5689 	 */
getUndoRedoOperationApprover(IUndoContext undoContext)5690 	protected IOperationApprover getUndoRedoOperationApprover(IUndoContext undoContext) {
5691 		return new NonLocalUndoUserApprover(undoContext, this, new Object [] { getEditorInput() }, Object.class);
5692 	}
5693 
5694 	/**
5695 	 * Creates this editor's standard actions and connects them with the global
5696 	 * workbench actions.
5697 	 * <p>
5698 	 * Subclasses may extend.</p>
5699 	 */
createActions()5700 	protected void createActions() {
5701 
5702 		ResourceAction action;
5703 
5704 		setAction(IWorkbenchCommandConstants.EDIT_CUT, null);
5705 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Cut.", this, ITextOperationTarget.CUT); //$NON-NLS-1$
5706 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
5707 		action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_CUT);
5708 		updateImages(action, IWorkbenchCommandConstants.EDIT_CUT);
5709 		setAction(ITextEditorActionConstants.CUT, action);
5710 
5711 		setAction(IWorkbenchCommandConstants.EDIT_COPY, null);
5712 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Copy.", this, ITextOperationTarget.COPY, true); //$NON-NLS-1$
5713 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
5714 		action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
5715 		updateImages(action, IWorkbenchCommandConstants.EDIT_COPY);
5716 		setAction(ITextEditorActionConstants.COPY, action);
5717 
5718 		setAction(IWorkbenchCommandConstants.EDIT_PASTE, null);
5719 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Paste.", this, ITextOperationTarget.PASTE); //$NON-NLS-1$
5720 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
5721 		action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE);
5722 		updateImages(action, IWorkbenchCommandConstants.EDIT_PASTE);
5723 		setAction(ITextEditorActionConstants.PASTE, action);
5724 
5725 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Delete.", this, ITextOperationTarget.DELETE); //$NON-NLS-1$
5726 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_ACTION);
5727 		action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_DELETE);
5728 		updateImages(action, IWorkbenchCommandConstants.EDIT_DELETE);
5729 		setAction(ITextEditorActionConstants.DELETE, action);
5730 
5731 		action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLine.", this, DeleteLineAction.WHOLE, false); //$NON-NLS-1$
5732 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_ACTION);
5733 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE);
5734 		setAction(ITextEditorActionConstants.DELETE_LINE, action);
5735 
5736 		action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLine.", this, DeleteLineAction.WHOLE, true); //$NON-NLS-1$
5737 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_ACTION);
5738 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE);
5739 		setAction(ITextEditorActionConstants.CUT_LINE, action);
5740 
5741 		action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, false); //$NON-NLS-1$
5742 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_BEGINNING_ACTION);
5743 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_BEGINNING);
5744 		setAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, action);
5745 
5746 		action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToBeginning.", this, DeleteLineAction.TO_BEGINNING, true); //$NON-NLS-1$
5747 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_BEGINNING_ACTION);
5748 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_BEGINNING);
5749 		setAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, action);
5750 
5751 		action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.DeleteLineToEnd.", this, DeleteLineAction.TO_END, false); //$NON-NLS-1$
5752 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.DELETE_LINE_TO_END_ACTION);
5753 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.DELETE_LINE_TO_END);
5754 		setAction(ITextEditorActionConstants.DELETE_LINE_TO_END, action);
5755 
5756 		action= new DeleteLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CutLineToEnd.", this, DeleteLineAction.TO_END, true); //$NON-NLS-1$
5757 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_LINE_TO_END_ACTION);
5758 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.CUT_LINE_TO_END);
5759 		setAction(ITextEditorActionConstants.CUT_LINE_TO_END, action);
5760 
5761 		action= new JoinLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.JoinLines.", this, " "); //$NON-NLS-1$ //$NON-NLS-2$
5762 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.JOIN_LINES_ACTION);
5763 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.JOIN_LINES);
5764 		setAction(ITextEditorActionConstants.JOIN_LINES, action);
5765 
5766 		action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SetMark.", this, MarkAction.SET_MARK); //$NON-NLS-1$
5767 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SET_MARK_ACTION);
5768 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SET_MARK);
5769 		setAction(ITextEditorActionConstants.SET_MARK, action);
5770 
5771 		action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ClearMark.", this, MarkAction.CLEAR_MARK); //$NON-NLS-1$
5772 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CLEAR_MARK_ACTION);
5773 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.CLEAR_MARK);
5774 		setAction(ITextEditorActionConstants.CLEAR_MARK, action);
5775 
5776 		action= new MarkAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SwapMark.", this, MarkAction.SWAP_MARK); //$NON-NLS-1$
5777 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SWAP_MARK_ACTION);
5778 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SWAP_MARK);
5779 		setAction(ITextEditorActionConstants.SWAP_MARK, action);
5780 
5781 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SelectAll.", this, ITextOperationTarget.SELECT_ALL, true); //$NON-NLS-1$
5782 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
5783 		action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
5784 		setAction(ITextEditorActionConstants.SELECT_ALL, action);
5785 
5786 		action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT); //$NON-NLS-1$
5787 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_RIGHT_ACTION);
5788 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_RIGHT);
5789 		setAction(ITextEditorActionConstants.SHIFT_RIGHT, action);
5790 
5791 		action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftRight.", this, ITextOperationTarget.SHIFT_RIGHT) { //$NON-NLS-1$
5792 			@Override
5793 			public void update() {
5794 				updateForTab();
5795 			}
5796 		};
5797 		setAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, action);
5798 
5799 		action= new ShiftAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShiftLeft.", this, ITextOperationTarget.SHIFT_LEFT); //$NON-NLS-1$
5800 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHIFT_LEFT_ACTION);
5801 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHIFT_LEFT);
5802 		setAction(ITextEditorActionConstants.SHIFT_LEFT, action);
5803 
5804 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Print.", this, ITextOperationTarget.PRINT, true); //$NON-NLS-1$
5805 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.PRINT_ACTION);
5806 		action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PRINT);
5807 		updateImages(action, IWorkbenchCommandConstants.FILE_PRINT);
5808 		setAction(ITextEditorActionConstants.PRINT, action);
5809 
5810 		action= new FindReplaceAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindReplace.", this); //$NON-NLS-1$
5811 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_ACTION);
5812 		action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
5813 		updateImages(action, IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
5814 		setAction(ITextEditorActionConstants.FIND, action);
5815 
5816 		action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindNext.", this, true); //$NON-NLS-1$
5817 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_NEXT_ACTION);
5818 		action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_NEXT);
5819 		setAction(ITextEditorActionConstants.FIND_NEXT, action);
5820 
5821 		action= new FindNextAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindPrevious.", this, false); //$NON-NLS-1$
5822 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_PREVIOUS_ACTION);
5823 		action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_PREVIOUS);
5824 		setAction(ITextEditorActionConstants.FIND_PREVIOUS, action);
5825 
5826 		action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncremental.", this, true); //$NON-NLS-1$
5827 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_ACTION);
5828 		action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL);
5829 		setAction(ITextEditorActionConstants.FIND_INCREMENTAL, action);
5830 
5831 		action= new IncrementalFindAction(EditorMessages.getBundleForConstructedKeys(), "Editor.FindIncrementalReverse.", this, false); //$NON-NLS-1$
5832 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.FIND_INCREMENTAL_REVERSE_ACTION);
5833 		action.setActionDefinitionId(IWorkbenchActionDefinitionIds.FIND_INCREMENTAL_REVERSE);
5834 		setAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, action);
5835 
5836 		fSaveAction= ActionFactory.SAVE.create(getSite().getWorkbenchWindow());
5837 		setAction(ITextEditorActionConstants.SAVE, fSaveAction);
5838 
5839 		action= new RevertToSavedAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Revert.", this); //$NON-NLS-1$
5840 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.REVERT_TO_SAVED_ACTION);
5841 		action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REVERT);
5842 		updateImages(action, IWorkbenchCommandConstants.FILE_REVERT);
5843 		setAction(ITextEditorActionConstants.REVERT_TO_SAVED, action);
5844 
5845 		action= new GotoLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.GotoLine.", this); //$NON-NLS-1$
5846 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.GOTO_LINE_ACTION);
5847 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.LINE_GOTO);
5848 		setAction(ITextEditorActionConstants.GOTO_LINE, action);
5849 
5850 		action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesUp.", this, getSourceViewer(), true, false); //$NON-NLS-1$
5851 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
5852 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_UP);
5853 		setAction(ITextEditorActionConstants.MOVE_LINE_UP, action);
5854 
5855 		action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.MoveLinesDown.", this, getSourceViewer(), false, false); //$NON-NLS-1$
5856 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.MOVE_LINES_ACTION);
5857 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.MOVE_LINES_DOWN);
5858 		setAction(ITextEditorActionConstants.MOVE_LINE_DOWN, action);
5859 
5860 		action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineUp.", this, getSourceViewer(), true, true); //$NON-NLS-1$
5861 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
5862 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_UP);
5863 		setAction(ITextEditorActionConstants.COPY_LINE_UP, action);
5864 
5865 		action= new MoveLinesAction(EditorMessages.getBundleForConstructedKeys(), "Editor.CopyLineDown.", this, getSourceViewer(), false, true); //$NON-NLS-1$
5866 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_LINES_ACTION);
5867 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.COPY_LINES_DOWN);
5868 		setAction(ITextEditorActionConstants.COPY_LINE_DOWN, action);
5869 
5870 		action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.UpperCase.", this, true); //$NON-NLS-1$
5871 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.UPPER_CASE_ACTION);
5872 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.UPPER_CASE);
5873 		setAction(ITextEditorActionConstants.UPPER_CASE, action);
5874 
5875 		action= new CaseAction(EditorMessages.getBundleForConstructedKeys(), "Editor.LowerCase.", this, false); //$NON-NLS-1$
5876 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.LOWER_CASE_ACTION);
5877 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.LOWER_CASE);
5878 		setAction(ITextEditorActionConstants.LOWER_CASE, action);
5879 
5880 		action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnter.", this, false); //$NON-NLS-1$
5881 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
5882 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER);
5883 		setAction(ITextEditorActionConstants.SMART_ENTER, action);
5884 
5885 		action= new InsertLineAction(EditorMessages.getBundleForConstructedKeys(), "Editor.SmartEnterInverse.", this, true); //$NON-NLS-1$
5886 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SMART_ENTER_ACTION);
5887 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SMART_ENTER_INVERSE);
5888 		setAction(ITextEditorActionConstants.SMART_ENTER_INVERSE, action);
5889 
5890 		action= new ToggleInsertModeAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleInsertMode."); //$NON-NLS-1$
5891 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.TOGGLE_INSERT_MODE_ACTION);
5892 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.TOGGLE_INSERT_MODE);
5893 		setAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE, action);
5894 
5895 		action= new HippieCompleteAction(EditorMessages.getBundleForConstructedKeys(), "Editor.HippieCompletion.", this); //$NON-NLS-1$
5896 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.HIPPIE_COMPLETION_ACTION);
5897 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.HIPPIE_COMPLETION);
5898 		setAction(ITextEditorActionConstants.HIPPIE_COMPLETION, action);
5899 
5900 		action= new ContentAssistAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ContentAssistProposal.", this); //$NON-NLS-1$
5901 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CONTENT_ASSIST_ACTION);
5902 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
5903 		setAction(ITextEditorActionConstants.CONTENT_ASSIST, action);
5904 		markAsStateDependentAction(ITextEditorActionConstants.CONTENT_ASSIST, true);
5905 
5906 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$
5907 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.CONTENT_ASSIST_CONTEXT_INFORMATION_ACTION);
5908 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION);
5909 		setAction(ITextEditorActionConstants.CONTENT_ASSIST_CONTEXT_INFORMATION, action);
5910 		markAsStateDependentAction(ITextEditorActionConstants.CONTENT_ASSIST_CONTEXT_INFORMATION, true);
5911 
5912 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.QuickAssist.", this, ISourceViewer.QUICK_ASSIST); //$NON-NLS-1$
5913 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.QUICK_ASSIST_ACTION);
5914 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
5915 		setAction(ITextEditorActionConstants.QUICK_ASSIST, action);
5916 		markAsStateDependentAction(ITextEditorActionConstants.QUICK_ASSIST, true);
5917 
5918 		action= new GotoAnnotationAction(this, true);
5919 		setAction(ITextEditorActionConstants.NEXT, action);
5920 		action= new GotoAnnotationAction(this, false);
5921 		setAction(ITextEditorActionConstants.PREVIOUS, action);
5922 
5923 		action= new RecenterAction(EditorMessages.getBundleForConstructedKeys(), "Editor.Recenter.", this); //$NON-NLS-1$
5924 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.RECENTER_ACTION);
5925 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.RECENTER);
5926 		setAction(ITextEditorActionConstants.RECENTER, action);
5927 
5928 		action= new ShowWhitespaceCharactersAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowWhitespaceCharacters.", this, getPreferenceStore()); //$NON-NLS-1$
5929 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_WHITESPACE_CHARACTERS_ACTION);
5930 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS);
5931 		setAction(ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS, action);
5932 
5933 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", this, ISourceViewer.INFORMATION, true); //$NON-NLS-1$
5934 		action= new InformationDispatchAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ShowInformation.", (TextOperationAction) action); //$NON-NLS-1$
5935 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.SHOW_INFORMATION_ACTION);
5936 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_INFORMATION);
5937 		setAction(ITextEditorActionConstants.SHOW_INFORMATION, action);
5938 
5939 		final BlockSelectionModeToggleAction blockAction= new BlockSelectionModeToggleAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleBlockSelectionMode.", this); //$NON-NLS-1$
5940 		action= blockAction;
5941 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.BLOCK_SELECTION_MODE_ACTION);
5942 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.BLOCK_SELECTION_MODE);
5943 		setAction(ITextEditorActionConstants.BLOCK_SELECTION_MODE, action);
5944 
5945 		if (isWordWrapSupported()) {
5946 			final WordWrapToggleAction wrapAction= new WordWrapToggleAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleWordWrap.", this, getInitialWordWrapStatus()); //$NON-NLS-1$
5947 			action= wrapAction;
5948 			action.setHelpContextId(IAbstractTextEditorHelpContextIds.WORD_WRAP_TOGGLE_ACTION);
5949 			action.setActionDefinitionId(ITextEditorActionDefinitionIds.WORD_WRAP);
5950 			setAction(ITextEditorActionConstants.WORD_WRAP, action);
5951 
5952 			blockAction.addPropertyChangeListener(event -> {
5953 				if (IAction.CHECKED == event.getProperty() && Boolean.TRUE.equals(event.getNewValue())) {
5954 					wrapAction.setChecked(false);
5955 				}
5956 			});
5957 			wrapAction.addPropertyChangeListener(event -> {
5958 				if (IAction.CHECKED == event.getProperty() && Boolean.TRUE.equals(event.getNewValue())) {
5959 					blockAction.setChecked(false);
5960 				}
5961 			});
5962 		}
5963 
5964 		action= new TextOperationAction(EditorMessages.getBundleForConstructedKeys(), "Editor.OpenHyperlink.", this, HyperlinkManager.OPEN_HYPERLINK, true); //$NON-NLS-1$;
5965 		action.setHelpContextId(IAbstractTextEditorHelpContextIds.OPEN_HYPERLINK_ACTION);
5966 		action.setActionDefinitionId(ITextEditorActionDefinitionIds.OPEN_HYPERLINK);
5967 		setAction(ITextEditorActionConstants.OPEN_HYPERLINK, action);
5968 
5969 		PropertyDialogAction openProperties= new PropertyDialogAction(
5970 				() -> getSite().getShell(),
5971 				new ISelectionProvider() {
5972 					@Override
5973 					public void addSelectionChangedListener(ISelectionChangedListener listener) {
5974 					}
5975 					@Override
5976 					public ISelection getSelection() {
5977 						return new StructuredSelection(getEditorInput());
5978 					}
5979 					@Override
5980 					public void removeSelectionChangedListener(ISelectionChangedListener listener) {
5981 					}
5982 					@Override
5983 					public void setSelection(ISelection selection) {
5984 					}
5985 				});
5986 		openProperties.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PROPERTIES);
5987 		updateImages(openProperties, IWorkbenchCommandConstants.FILE_PROPERTIES);
5988 		setAction(ITextEditorActionConstants.PROPERTIES, openProperties);
5989 
5990 		markAsContentDependentAction(ITextEditorActionConstants.UNDO, true);
5991 		markAsContentDependentAction(ITextEditorActionConstants.REDO, true);
5992 		markAsContentDependentAction(ITextEditorActionConstants.FIND, true);
5993 		markAsContentDependentAction(ITextEditorActionConstants.FIND_NEXT, true);
5994 		markAsContentDependentAction(ITextEditorActionConstants.FIND_PREVIOUS, true);
5995 		markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL, true);
5996 		markAsContentDependentAction(ITextEditorActionConstants.FIND_INCREMENTAL_REVERSE, true);
5997 
5998 		markAsSelectionDependentAction(ITextEditorActionConstants.CUT, true);
5999 		markAsSelectionDependentAction(ITextEditorActionConstants.COPY, true);
6000 		markAsSelectionDependentAction(ITextEditorActionConstants.PASTE, true);
6001 		markAsSelectionDependentAction(ITextEditorActionConstants.DELETE, true);
6002 		markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
6003 		markAsSelectionDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
6004 		markAsSelectionDependentAction(ITextEditorActionConstants.UPPER_CASE, true);
6005 		markAsSelectionDependentAction(ITextEditorActionConstants.LOWER_CASE, true);
6006 
6007 		markAsPropertyDependentAction(ITextEditorActionConstants.UNDO, true);
6008 		markAsPropertyDependentAction(ITextEditorActionConstants.REDO, true);
6009 		markAsPropertyDependentAction(ITextEditorActionConstants.REVERT_TO_SAVED, true);
6010 		markAsPropertyDependentAction(ITextEditorActionConstants.SAVE, true);
6011 
6012 		markAsStateDependentAction(ITextEditorActionConstants.UNDO, true);
6013 		markAsStateDependentAction(ITextEditorActionConstants.REDO, true);
6014 		markAsStateDependentAction(ITextEditorActionConstants.CUT, true);
6015 		markAsStateDependentAction(ITextEditorActionConstants.PASTE, true);
6016 		markAsStateDependentAction(ITextEditorActionConstants.DELETE, true);
6017 		markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT, true);
6018 		markAsStateDependentAction(ITextEditorActionConstants.SHIFT_RIGHT_TAB, true);
6019 		markAsStateDependentAction(ITextEditorActionConstants.SHIFT_LEFT, true);
6020 		markAsStateDependentAction(ITextEditorActionConstants.FIND, true);
6021 		markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE, true);
6022 		markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_BEGINNING, true);
6023 		markAsStateDependentAction(ITextEditorActionConstants.DELETE_LINE_TO_END, true);
6024 		markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_UP, true);
6025 		markAsStateDependentAction(ITextEditorActionConstants.MOVE_LINE_DOWN, true);
6026 		markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE, true);
6027 		markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_BEGINNING, true);
6028 		markAsStateDependentAction(ITextEditorActionConstants.CUT_LINE_TO_END, true);
6029 
6030 		setActionActivationCode(ITextEditorActionConstants.SHIFT_RIGHT_TAB,'\t', -1, SWT.NONE);
6031 		setActionActivationCode(ITextEditorActionConstants.SHIFT_LEFT, '\t', -1, SWT.SHIFT);
6032 	}
6033 
updateImages(IAction action, String commandId)6034 	private void updateImages(IAction action, String commandId) {
6035 		if (action.getImageDescriptor() != null) {
6036 			return;
6037 		}
6038 		ICommandImageService imgService = getSite().getService(ICommandImageService.class);
6039 		if (imgService == null) {
6040 			return;
6041 		}
6042 		action.setImageDescriptor(imgService.getImageDescriptor(commandId));
6043 		action.setDisabledImageDescriptor(imgService.getImageDescriptor(commandId, ICommandImageService.TYPE_DISABLED));
6044 		action.setHoverImageDescriptor(imgService.getImageDescriptor(commandId, ICommandImageService.TYPE_HOVER));
6045 	}
6046 
6047 	/**
6048 	 * Convenience method to add the action installed under the given action id to the given menu.
6049 	 * @param menu the menu to add the action to
6050 	 * @param actionId the id of the action to be added
6051 	 */
addAction(IMenuManager menu, String actionId)6052 	protected final void addAction(IMenuManager menu, String actionId) {
6053 		IAction action= getAction(actionId);
6054 		if (action != null) {
6055 			if (action instanceof IUpdate)
6056 				((IUpdate) action).update();
6057 			menu.add(action);
6058 		}
6059 	}
6060 
6061 	/**
6062 	 * Convenience method to add the action installed under the given action id to the specified group of the menu.
6063 	 * @param menu the menu to add the action to
6064 	 * @param group the group in the menu
6065 	 * @param actionId the id of the action to add
6066 	 */
addAction(IMenuManager menu, String group, String actionId)6067 	protected final void addAction(IMenuManager menu, String group, String actionId) {
6068 	 	IAction action= getAction(actionId);
6069 	 	if (action != null) {
6070 	 		if (action instanceof IUpdate)
6071 	 			((IUpdate) action).update();
6072 
6073 	 		IMenuManager subMenu= menu.findMenuUsingPath(group);
6074 	 		if (subMenu != null)
6075 	 			subMenu.add(action);
6076 	 		else
6077 	 			menu.appendToGroup(group, action);
6078 	 	}
6079 	}
6080 
6081 	/**
6082 	 * Convenience method to add a new group after the specified group.
6083 	 * @param menu the menu to add the new group to
6084 	 * @param existingGroup the group after which to insert the new group
6085 	 * @param newGroup the new group
6086 	 */
addGroup(IMenuManager menu, String existingGroup, String newGroup)6087 	protected final void addGroup(IMenuManager menu, String existingGroup, String newGroup) {
6088 		IMenuManager subMenu= menu.findMenuUsingPath(existingGroup);
6089 		if (subMenu != null)
6090 			subMenu.add(new Separator(newGroup));
6091 		else
6092 			menu.appendToGroup(existingGroup, new Separator(newGroup));
6093 	}
6094 
6095 	/**
6096 	 * Sets up the ruler context menu before it is made visible.
6097 	 * <p>
6098 	 * Subclasses may extend to add other actions.</p>
6099 	 *
6100 	 * @param menu the menu
6101 	 */
rulerContextMenuAboutToShow(IMenuManager menu)6102 	protected void rulerContextMenuAboutToShow(IMenuManager menu) {
6103 
6104 		menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
6105 		menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
6106 
6107 		for (Iterator<IMenuListener> i= fRulerContextMenuListeners.iterator(); i.hasNext();)
6108 			i.next().menuAboutToShow(menu);
6109 
6110 		addAction(menu, ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS);
6111 		addAction(menu, ITextEditorActionConstants.RULER_MANAGE_TASKS);
6112 	}
6113 
6114 	/**
6115 	 * Sets up this editor's context menu before it is made visible.
6116 	 * <p>
6117 	 * Subclasses may extend to add other actions.</p>
6118 	 *
6119 	 * @param menu the menu
6120 	 */
editorContextMenuAboutToShow(IMenuManager menu)6121 	protected void editorContextMenuAboutToShow(IMenuManager menu) {
6122 
6123 		menu.add(new Separator(ITextEditorActionConstants.GROUP_UNDO));
6124 		menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_SAVE));
6125 		menu.add(new Separator(ITextEditorActionConstants.GROUP_COPY));
6126 		menu.add(new Separator(ITextEditorActionConstants.GROUP_PRINT));
6127 		menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
6128 		menu.add(new Separator(ITextEditorActionConstants.GROUP_FIND));
6129 		menu.add(new Separator(IWorkbenchActionConstants.GROUP_ADD));
6130 		menu.add(new Separator(ITextEditorActionConstants.GROUP_REST));
6131 		menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
6132 
6133 		if (isEditable()) {
6134 			addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.UNDO);
6135 			addAction(menu, ITextEditorActionConstants.GROUP_UNDO, ITextEditorActionConstants.REVERT_TO_SAVED);
6136 			addAction(menu, ITextEditorActionConstants.GROUP_SAVE, ITextEditorActionConstants.SAVE);
6137 			addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.CUT);
6138 			addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
6139 			addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.PASTE);
6140 			IAction action= getAction(ITextEditorActionConstants.QUICK_ASSIST);
6141 			if (action != null && action.isEnabled())
6142 				addAction(menu, ITextEditorActionConstants.GROUP_EDIT, ITextEditorActionConstants.QUICK_ASSIST);
6143 		} else {
6144 			addAction(menu, ITextEditorActionConstants.GROUP_COPY, ITextEditorActionConstants.COPY);
6145 		}
6146 	}
6147 
6148 	/**
6149 	 * Returns the status line manager of this editor.
6150 	 *
6151 	 * @return the status line manager of this editor
6152 	 * @since 2.0, protected since 3.3
6153 	 */
getStatusLineManager()6154 	protected IStatusLineManager getStatusLineManager() {
6155 		return getEditorSite().getActionBars().getStatusLineManager();
6156 	}
6157 
6158 	@SuppressWarnings("unchecked")
6159 	@Override
getAdapter(Class<T> required)6160 	public <T> T getAdapter(Class<T> required) {
6161 
6162 		if (IEditorStatusLine.class.equals(required)) {
6163 			if (fEditorStatusLine == null) {
6164 				IStatusLineManager statusLineManager= getStatusLineManager();
6165 				ISelectionProvider selectionProvider= getSelectionProvider();
6166 				if (statusLineManager != null && selectionProvider != null)
6167 					fEditorStatusLine= new EditorStatusLine(statusLineManager, selectionProvider);
6168 			}
6169 			return (T) fEditorStatusLine;
6170 		}
6171 
6172 		if (IVerticalRulerInfo.class.equals(required)) {
6173 			if (fVerticalRuler != null)
6174 				return (T) fVerticalRuler;
6175 		}
6176 
6177 		if (IMarkRegionTarget.class.equals(required)) {
6178 			if (fMarkRegionTarget == null) {
6179 				IStatusLineManager manager= getStatusLineManager();
6180 				if (manager != null)
6181 					fMarkRegionTarget= (fSourceViewer == null ? null : new MarkRegionTarget(fSourceViewer, manager));
6182 			}
6183 			return (T) fMarkRegionTarget;
6184 		}
6185 
6186 		if (IDeleteLineTarget.class.equals(required)) {
6187 			if (fDeleteLineTarget == null) {
6188 				fDeleteLineTarget= new TextViewerDeleteLineTarget(fSourceViewer);
6189 			}
6190 			return (T) fDeleteLineTarget;
6191 		}
6192 
6193 		if (IncrementalFindTarget.class.equals(required)) {
6194 			if (fIncrementalFindTarget == null) {
6195 				IStatusLineManager manager= getStatusLineManager();
6196 				if (manager != null)
6197 					fIncrementalFindTarget= (fSourceViewer == null ? null : new IncrementalFindTarget(fSourceViewer, manager));
6198 			}
6199 			return (T) fIncrementalFindTarget;
6200 		}
6201 
6202 		if (IFindReplaceTarget.class.equals(required)) {
6203 			if (fFindReplaceTarget == null) {
6204 				IFindReplaceTarget target= (fSourceViewer == null ? null : fSourceViewer.getFindReplaceTarget());
6205 				if (target != null) {
6206 					fFindReplaceTarget= new FindReplaceTarget(this, target);
6207 					if (fFindScopeHighlightColor != null)
6208 						fFindReplaceTarget.setScopeHighlightColor(fFindScopeHighlightColor);
6209 				}
6210 			}
6211 			return (T) fFindReplaceTarget;
6212 		}
6213 
6214 		if (ITextOperationTarget.class.equals(required))
6215 			return (fSourceViewer == null ? null : (T) fSourceViewer.getTextOperationTarget());
6216 
6217 		if (IRewriteTarget.class.equals(required)) {
6218 			if (fSourceViewer instanceof ITextViewerExtension) {
6219 				ITextViewerExtension extension= (ITextViewerExtension) fSourceViewer;
6220 				return (T) extension.getRewriteTarget();
6221 			}
6222 			return null;
6223 		}
6224 
6225 		if (Control.class.equals(required))
6226 			return fSourceViewer != null ? (T) fSourceViewer.getTextWidget() : null;
6227 
6228 		if (IColumnSupport.class.equals(required)) {
6229 			if (fColumnSupport == null)
6230 				fColumnSupport= createColumnSupport();
6231 			return (T) fColumnSupport;
6232 		}
6233 
6234 		if (ITextViewer.class.equals(required))
6235 			return (fSourceViewer == null ? null : (T) fSourceViewer);
6236 
6237 		return super.getAdapter(required);
6238 	}
6239 
6240 	@Override
setFocus()6241 	public void setFocus() {
6242 		if (fSourceViewer != null) {
6243 			StyledText widget= fSourceViewer.getTextWidget();
6244 			if (widget != null && !widget.isDisposed()) {
6245 				widget.setFocus();
6246 			}
6247 		}
6248 	}
6249 
6250 	@Override
showsHighlightRangeOnly()6251 	public boolean showsHighlightRangeOnly() {
6252 		return fShowHighlightRangeOnly;
6253 	}
6254 
6255 	@Override
showHighlightRangeOnly(boolean showHighlightRangeOnly)6256 	public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
6257 		fShowHighlightRangeOnly= showHighlightRangeOnly;
6258 	}
6259 
6260 	@Override
setHighlightRange(int offset, int length, boolean moveCursor)6261 	public void setHighlightRange(int offset, int length, boolean moveCursor) {
6262 		if (fSourceViewer == null)
6263 			return;
6264 
6265 		if (fShowHighlightRangeOnly) {
6266 			if (moveCursor)
6267 				fSourceViewer.setVisibleRegion(offset, length);
6268 		} else {
6269 			IRegion rangeIndication= fSourceViewer.getRangeIndication();
6270 			if (rangeIndication == null || offset != rangeIndication.getOffset() || length != rangeIndication.getLength())
6271 				fSourceViewer.setRangeIndication(offset, length, moveCursor);
6272 		}
6273 	}
6274 
6275 	@Override
getHighlightRange()6276 	public IRegion getHighlightRange() {
6277 		if (fSourceViewer == null)
6278 			return null;
6279 
6280 		if (fShowHighlightRangeOnly)
6281 			return getCoverage(fSourceViewer);
6282 
6283 		return fSourceViewer.getRangeIndication();
6284 	}
6285 
6286 	/*
6287 	 * @see ITextEditor#resetHighlightRange
6288 	 */
6289 	@Override
resetHighlightRange()6290 	public void resetHighlightRange() {
6291 		if (fSourceViewer == null)
6292 			return;
6293 
6294 		if (fShowHighlightRangeOnly)
6295 			fSourceViewer.resetVisibleRegion();
6296 		else
6297 			fSourceViewer.removeRangeIndication();
6298 	}
6299 
6300 	/**
6301 	 * Adjusts the highlight range so that at least the specified range
6302 	 * is highlighted.
6303 	 * <p>
6304 	 * Subclasses may re-implement this method.</p>
6305 	 *
6306 	 * @param offset the offset of the range which at least should be highlighted
6307 	 * @param length the length of the range which at least should be highlighted
6308 	 */
adjustHighlightRange(int offset, int length)6309 	protected void adjustHighlightRange(int offset, int length) {
6310 		if (fSourceViewer == null)
6311 			return;
6312 
6313 		if (fSourceViewer instanceof ITextViewerExtension5) {
6314 			ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
6315 			extension.exposeModelRange(new Region(offset, length));
6316 		} else if (!isVisible(fSourceViewer, offset, length)) {
6317 			fSourceViewer.resetVisibleRegion();
6318 		}
6319 	}
6320 
6321 	@Override
selectAndReveal(int start, int length)6322 	public void selectAndReveal(int start, int length) {
6323 		selectAndReveal(start, length, start, length);
6324 	}
6325 
6326 	/**
6327 	 * Selects and reveals the specified ranges in this text editor.
6328 	 *
6329 	 * @param selectionStart the offset of the selection
6330 	 * @param selectionLength the length of the selection
6331 	 * @param revealStart the offset of the revealed range
6332 	 * @param revealLength the length of the revealed range
6333 	 * @since 3.0
6334 	 */
selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength)6335 	protected void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) {
6336 		if (fSourceViewer == null)
6337 			return;
6338 
6339 		ISelection selection= getSelectionProvider().getSelection();
6340 		if (selection instanceof ITextSelection) {
6341 			ITextSelection textSelection= (ITextSelection) selection;
6342 			if (textSelection.getOffset() != 0 || textSelection.getLength() != 0)
6343 				markInNavigationHistory();
6344 		}
6345 
6346 		StyledText widget= fSourceViewer.getTextWidget();
6347 		widget.setRedraw(false);
6348 		adjustHighlightRange(revealStart, revealLength);
6349 		fSourceViewer.revealRange(revealStart, revealLength);
6350 
6351 		fSourceViewer.setSelectedRange(selectionStart, selectionLength);
6352 
6353 		markInNavigationHistory();
6354 		widget.setRedraw(true);
6355 	}
6356 
6357 	/*
6358 	 * @see org.eclipse.ui.INavigationLocationProvider#createNavigationLocation()
6359 	 * @since 2.1
6360 	 */
6361 	@Override
createEmptyNavigationLocation()6362 	public INavigationLocation createEmptyNavigationLocation() {
6363 		return new TextSelectionNavigationLocation(this, false);
6364 	}
6365 
6366 	@Override
createNavigationLocation()6367 	public INavigationLocation createNavigationLocation() {
6368 		return new TextSelectionNavigationLocation(this, true);
6369 	}
6370 
6371 	/**
6372 	 * Writes a check mark of the given situation into the navigation history.
6373 	 * @since 2.1
6374 	 */
markInNavigationHistory()6375 	protected void markInNavigationHistory() {
6376 		getSite().getPage().getNavigationHistory().markLocation(this);
6377 	}
6378 
6379 	/**
6380 	 * Hook which gets called when the editor has been saved.
6381 	 * Subclasses may extend.
6382 	 * @since 2.1
6383 	 */
editorSaved()6384 	protected void editorSaved() {
6385 		INavigationLocation[] locations= getSite().getPage().getNavigationHistory().getLocations();
6386 		IEditorInput input= getEditorInput();
6387 		for (int i= 0; i < locations.length; i++) {
6388 			if (locations[i] instanceof TextSelectionNavigationLocation) {
6389 				if(input.equals(locations[i].getInput())) {
6390 					TextSelectionNavigationLocation location= (TextSelectionNavigationLocation) locations[i];
6391 					location.partSaved(this);
6392 				}
6393 			}
6394 		}
6395 	}
6396 
6397 	@Override
firePropertyChange(int property)6398 	protected void firePropertyChange(int property) {
6399 		super.firePropertyChange(property);
6400 		updatePropertyDependentActions();
6401 	}
6402 
6403 	@Override
setStatusField(IStatusField field, String category)6404 	public void setStatusField(IStatusField field, String category) {
6405 		Assert.isNotNull(category);
6406 		if (field != null) {
6407 
6408 			if (fStatusFields == null)
6409 				fStatusFields= new HashMap<>(3);
6410 
6411 			fStatusFields.put(category, field);
6412 			updateStatusField(category);
6413 
6414 		} else if (fStatusFields != null)
6415 			fStatusFields.remove(category);
6416 
6417 		if (fIncrementalFindTarget != null && ITextEditorActionConstants.STATUS_CATEGORY_FIND_FIELD.equals(category))
6418 			fIncrementalFindTarget.setStatusField(field);
6419 	}
6420 
6421 	/**
6422 	 * Returns the current status field for the given status category.
6423 	 *
6424 	 * @param category the status category
6425 	 * @return the current status field for the given status category
6426 	 * @since 2.0
6427 	 */
getStatusField(String category)6428 	protected IStatusField getStatusField(String category) {
6429 		if (category != null && fStatusFields != null)
6430 			return fStatusFields.get(category);
6431 		return null;
6432 	}
6433 
6434 	/**
6435 	 * Returns whether this editor is in overwrite or insert mode.
6436 	 *
6437 	 * @return <code>true</code> if in insert mode, <code>false</code> for overwrite mode
6438 	 * @since 2.0
6439 	 */
isInInsertMode()6440 	protected boolean isInInsertMode() {
6441 		return !fIsOverwriting;
6442 	}
6443 
6444 	@Override
getInsertMode()6445 	public InsertMode getInsertMode() {
6446 		return fInsertMode;
6447 	}
6448 
6449 	@Override
setInsertMode(InsertMode newMode)6450 	public void setInsertMode(InsertMode newMode) {
6451 		List<InsertMode> legalModes= getLegalInsertModes();
6452 		if (!legalModes.contains(newMode))
6453 			throw new IllegalArgumentException();
6454 
6455 		fInsertMode= newMode;
6456 
6457 		handleInsertModeChanged();
6458 	}
6459 
6460 	/**
6461 	 * Returns the set of legal insert modes. If insert modes are configured all defined insert modes
6462 	 * are legal.
6463 	 *
6464 	 * @return the set of legal insert modes
6465 	 * @since 3.0
6466 	 */
getLegalInsertModes()6467 	protected List<InsertMode> getLegalInsertModes() {
6468 		if (fLegalInsertModes == null) {
6469 			fLegalInsertModes= new ArrayList<>();
6470 			fLegalInsertModes.add(SMART_INSERT);
6471 			fLegalInsertModes.add(INSERT);
6472 		}
6473 		return fLegalInsertModes;
6474 	}
6475 
switchToNextInsertMode()6476 	private void switchToNextInsertMode() {
6477 
6478 		InsertMode mode= getInsertMode();
6479 		List<InsertMode> legalModes= getLegalInsertModes();
6480 
6481 		int i= 0;
6482 		while (i < legalModes.size()) {
6483 			if (legalModes.get(i) == mode) break;
6484 			++ i;
6485 		}
6486 
6487 		i= (i + 1) % legalModes.size();
6488 		InsertMode newMode= legalModes.get(i);
6489 		setInsertMode(newMode);
6490 	}
6491 
toggleOverwriteMode()6492 	private void toggleOverwriteMode() {
6493 		if (fIsOverwriteModeEnabled) {
6494 			fIsOverwriting= !fIsOverwriting;
6495 			fSourceViewer.getTextWidget().invokeAction(ST.TOGGLE_OVERWRITE);
6496 			handleInsertModeChanged();
6497 		}
6498 	}
6499 
6500 	/**
6501 	 * Configures the given insert mode as legal or illegal. This call is ignored if the set of legal
6502 	 * input modes would be empty after the call.
6503 	 *
6504 	 * @param mode the insert mode to be configured
6505 	 * @param legal <code>true</code> if the given mode is legal, <code>false</code> otherwise
6506 	 * @since 3.0
6507 	 */
configureInsertMode(InsertMode mode, boolean legal)6508 	protected void configureInsertMode(InsertMode mode, boolean legal) {
6509 		List<InsertMode> legalModes= getLegalInsertModes();
6510 		if (legal) {
6511 			if (!legalModes.contains(mode))
6512 				legalModes.add(mode);
6513 		} else if (legalModes.size() > 1) {
6514 			if (getInsertMode() == mode)
6515 				switchToNextInsertMode();
6516 			legalModes.remove(mode);
6517 		}
6518 	}
6519 
6520 	/**
6521 	 * Sets the overwrite mode enablement.
6522 	 *
6523 	 * @param enable <code>true</code> to enable new overwrite mode,
6524 	 *        <code>false</code> to disable
6525 	 * @since 3.0
6526 	 */
enableOverwriteMode(boolean enable)6527 	protected void enableOverwriteMode(boolean enable) {
6528 		if (fIsOverwriting && !enable)
6529 			toggleOverwriteMode();
6530 		fIsOverwriteModeEnabled= enable;
6531 	}
6532 
createOverwriteCaret(StyledText styledText)6533 	private Caret createOverwriteCaret(StyledText styledText) {
6534 		Caret caret= new Caret(styledText, SWT.NULL);
6535 		GC gc= new GC(styledText);
6536 		// XXX: this overwrite box is not proportional-font aware
6537 		// take 'a' as a medium sized character
6538 		Point charSize= gc.stringExtent("a"); //$NON-NLS-1$
6539 
6540 		// XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6541 		caret.setSize(charSize.x, styledText.getLineHeight());
6542 		caret.setFont(styledText.getFont());
6543 
6544 		gc.dispose();
6545 
6546 		return caret;
6547 	}
6548 
createInsertCaret(StyledText styledText)6549 	private Caret createInsertCaret(StyledText styledText) {
6550 		Caret caret= new Caret(styledText, SWT.NULL);
6551 
6552 		// XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6553 		caret.setSize(getCaretWidthPreference(), styledText.getLineHeight());
6554 		caret.setFont(styledText.getFont());
6555 
6556 		return caret;
6557 	}
6558 
createRawInsertModeCaretImage(StyledText styledText)6559 	private Image createRawInsertModeCaretImage(StyledText styledText) {
6560 
6561 		PaletteData caretPalette= new PaletteData(new RGB[] {new RGB (0,0,0), new RGB (255,255,255)});
6562 		int width= getCaretWidthPreference();
6563 		int widthOffset= width - 1;
6564 
6565 		// XXX: Filed request to get a caret with auto-height: https://bugs.eclipse.org/bugs/show_bug.cgi?id=118612
6566 		ImageData imageData= new ImageData(4 + widthOffset, styledText.getLineHeight(), 1, caretPalette);
6567 
6568 		Display display= styledText.getDisplay();
6569 		Image bracketImage= new Image(display, imageData);
6570 		GC gc= new GC (bracketImage);
6571 		gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
6572 		gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
6573 		int height= imageData.height / 3;
6574 		// gap between two bars of one third of the height
6575 		// draw boxes using lines as drawing a line of a certain width produces
6576 		// rounded corners.
6577 		for (int i= 0; i < width ; i++) {
6578 			gc.drawLine(i, 0, i, height - 1);
6579 			gc.drawLine(i, imageData.height - height, i, imageData.height - 1);
6580 		}
6581 
6582 		gc.dispose();
6583 
6584 		return bracketImage;
6585 	}
6586 
createRawInsertModeCaret(StyledText styledText)6587 	private Caret createRawInsertModeCaret(StyledText styledText) {
6588 		// don't draw special raw caret if no smart mode is enabled
6589 		if (!getLegalInsertModes().contains(SMART_INSERT))
6590 			return createInsertCaret(styledText);
6591 
6592 		Caret caret= new Caret(styledText, SWT.NULL);
6593 		Image image= createRawInsertModeCaretImage(styledText);
6594 		caret.setImage(image);
6595 		caret.setFont(styledText.getFont());
6596 
6597 		return caret;
6598 	}
6599 
getCaretWidthPreference()6600 	private int getCaretWidthPreference() {
6601 		if (getPreferenceStore() != null && getPreferenceStore().getBoolean(PREFERENCE_WIDE_CARET))
6602 			return WIDE_CARET_WIDTH;
6603 
6604 		return SINGLE_CARET_WIDTH;
6605 	}
6606 
updateCaret()6607 	private void updateCaret() {
6608 		if (fSourceViewer == null || fSourceViewer.getTextWidget() == null) {
6609 			return;
6610 		}
6611 
6612 		StyledText styledText= fSourceViewer.getTextWidget();
6613 
6614 		InsertMode mode= getInsertMode();
6615 
6616 		styledText.setCaret(null);
6617 		disposeNonDefaultCaret();
6618 
6619 		if (getPreferenceStore() == null || !getPreferenceStore().getBoolean(PREFERENCE_USE_CUSTOM_CARETS))
6620 			Assert.isTrue(fNonDefaultCaret == null);
6621 		else if (fIsOverwriting)
6622 			fNonDefaultCaret= createOverwriteCaret(styledText);
6623 		else if (SMART_INSERT == mode)
6624 			fNonDefaultCaret= createInsertCaret(styledText);
6625 		else if (INSERT == mode)
6626 			fNonDefaultCaret= createRawInsertModeCaret(styledText);
6627 
6628 		if (fNonDefaultCaret != null) {
6629 			styledText.setCaret(fNonDefaultCaret);
6630 			fNonDefaultCaretImage= fNonDefaultCaret.getImage();
6631 		} else if (fInitialCaret != styledText.getCaret())
6632 			styledText.setCaret(fInitialCaret);
6633 	}
6634 
disposeNonDefaultCaret()6635 	private void disposeNonDefaultCaret() {
6636 		if (fNonDefaultCaretImage != null) {
6637 			fNonDefaultCaretImage.dispose();
6638 			fNonDefaultCaretImage= null;
6639 		}
6640 
6641 		if (fNonDefaultCaret != null) {
6642 			fNonDefaultCaret.dispose();
6643 			fNonDefaultCaret= null;
6644 		}
6645 	}
6646 
6647 	/**
6648 	 * Handles a change of the editor's insert mode.
6649 	 * Subclasses may extend.
6650 	 *
6651 	 * @since 2.0
6652 	 */
handleInsertModeChanged()6653 	protected void handleInsertModeChanged() {
6654 		updateInsertModeAction();
6655 		updateCaret();
6656 		updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE);
6657 	}
6658 
updateInsertModeAction()6659 	private void updateInsertModeAction() {
6660 
6661 		// this may be called before the part is fully initialized (see configureInsertMode)
6662 		// drop out in this case.
6663 		if (getSite() == null)
6664 			return;
6665 
6666 		IAction action= getAction(ITextEditorActionConstants.TOGGLE_INSERT_MODE);
6667 		if (action != null) {
6668 			action.setEnabled(!fIsOverwriting);
6669 			action.setChecked(fInsertMode == SMART_INSERT);
6670 		}
6671 	}
6672 
6673 	/**
6674 	 * Handles a potential change of the cursor position.
6675 	 * Subclasses may extend.
6676 	 *
6677 	 * @since 2.0
6678 	 */
handleCursorPositionChanged()6679 	protected void handleCursorPositionChanged() {
6680 		updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
6681 	}
6682 
6683 	/**
6684 	 * Updates the status fields for the given category.
6685 	 *
6686 	 * @param category the category
6687 	 * @since 2.0
6688 	 */
updateStatusField(String category)6689 	protected void updateStatusField(String category) {
6690 
6691 		if (category == null)
6692 			return;
6693 
6694 		IStatusField field= getStatusField(category);
6695 		if (field != null) {
6696 
6697 			String text= null;
6698 
6699 			switch (category) {
6700 			case ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION:
6701 				text= getCursorPosition();
6702 				break;
6703 			case ITextEditorActionConstants.STATUS_CATEGORY_ELEMENT_STATE:
6704 				text= isEditorInputReadOnly() ? fReadOnlyLabel : fWritableLabel;
6705 				break;
6706 			case ITextEditorActionConstants.STATUS_CATEGORY_INPUT_MODE:
6707 				InsertMode mode= getInsertMode();
6708 				if (fIsOverwriting)
6709 					text= fOverwriteModeLabel;
6710 				else if (INSERT == mode)
6711 					text= fInsertModeLabel;
6712 				else if (SMART_INSERT == mode)
6713 					text= fSmartInsertModeLabel;
6714 				break;
6715 			default:
6716 				break;
6717 			}
6718 
6719 			field.setText(text == null ? fErrorLabel : text);
6720 		}
6721 	}
6722 
6723 	/**
6724 	 * Updates all status fields.
6725 	 *
6726 	 * @since 2.0
6727 	 */
updateStatusFields()6728 	protected void updateStatusFields() {
6729 		if (fStatusFields != null) {
6730 			Iterator<String> e= fStatusFields.keySet().iterator();
6731 			while (e.hasNext())
6732 				updateStatusField(e.next());
6733 		}
6734 	}
6735 
6736 	/**
6737 	 * Returns a description of the cursor position.
6738 	 *
6739 	 * @return a description of the cursor position
6740 	 * @since 2.0
6741 	 */
getCursorPosition()6742 	protected String getCursorPosition() {
6743 
6744 		if (fSourceViewer == null)
6745 			return fErrorLabel;
6746 
6747 		StyledText styledText= fSourceViewer.getTextWidget();
6748 		int caret= widgetOffset2ModelOffset(fSourceViewer, styledText.getCaretOffset());
6749 		IDocument document= fSourceViewer.getDocument();
6750 		if (document == null)
6751 			return fErrorLabel;
6752 
6753 		try {
6754 
6755 			int line= document.getLineOfOffset(caret);
6756 
6757 			int lineOffset= document.getLineOffset(line);
6758 			int tabWidth= styledText.getTabs();
6759 			int column= 0;
6760 			for (int i= lineOffset; i < caret; i++)
6761 				if ('\t' == document.getChar(i))
6762 					column += tabWidth - (tabWidth == 0 ? 0 : column % tabWidth);
6763 				else
6764 					column++;
6765 
6766 			fLineLabel.fValue= line + 1;
6767 			fColumnLabel.fValue= column + 1;
6768 			boolean showSelection = getPreferenceStore().getBoolean(PREFERENCE_SHOW_SELECTION_SIZE);
6769 			boolean showOffset = getPreferenceStore().getBoolean(PREFERENCE_SHOW_CARET_OFFSET);
6770 			Point selectedRange = fSourceViewer.getSelectedRange();
6771 			int selectionLength = selectedRange != null ? selectedRange.y : 0;
6772 			showSelection = showSelection && selectionLength > 0;
6773 			fOffsetLabel.fValue = showSelection ? selectionLength : caret;
6774 			if (!showSelection) {
6775 				if (!showOffset) {
6776 					// shows line : column
6777 					return NLSUtility.format(fPositionLabelPattern, fPositionLabelPatternArguments);
6778 				}
6779 				// shows line : column : offset
6780 				return NLSUtility.format(EditorMessages.Editor_statusline_position_pattern_offset,
6781 						fPositionLabelPatternArguments);
6782 			}
6783 			// To show *right* selection, we first need to know if we are in the
6784 			// block selection mode or not
6785 			if (isBlockSelectionModeSupported() && isBlockSelectionModeEnabled()) {
6786 				BlockTextSelection block = (BlockTextSelection) fSourceViewer.getSelectionProvider().getSelection();
6787 				AtomicInteger sum = new AtomicInteger();
6788 				Arrays.asList(block.getRegions()).forEach(r -> sum.addAndGet(r.getLength()));
6789 				fOffsetLabel.fValue = sum.intValue();
6790 			}
6791 			// shows line : column [selection size]
6792 			return NLSUtility.format(EditorMessages.Editor_statusline_position_pattern_selection,
6793 					fPositionLabelPatternArguments);
6794 		} catch (BadLocationException x) {
6795 			return fErrorLabel;
6796 		}
6797 	}
6798 
6799 	@Override
isEditorInputReadOnly()6800 	public boolean isEditorInputReadOnly() {
6801 		IDocumentProvider provider= getDocumentProvider();
6802 		if (provider instanceof IDocumentProviderExtension) {
6803 			IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
6804 			return extension.isReadOnly(getEditorInput());
6805 		}
6806 		return true;
6807 	}
6808 
6809 	@Override
isEditorInputModifiable()6810 	public boolean isEditorInputModifiable() {
6811 		IDocumentProvider provider= getDocumentProvider();
6812 		if (provider instanceof IDocumentProviderExtension) {
6813 			IDocumentProviderExtension extension= (IDocumentProviderExtension) provider;
6814 			return extension.isModifiable(getEditorInput());
6815 		}
6816 		return true;
6817 	}
6818 
6819 	@Override
addRulerContextMenuListener(IMenuListener listener)6820 	public void addRulerContextMenuListener(IMenuListener listener) {
6821 		fRulerContextMenuListeners.add(listener);
6822 	}
6823 
6824 	@Override
removeRulerContextMenuListener(IMenuListener listener)6825 	public void removeRulerContextMenuListener(IMenuListener listener) {
6826 		fRulerContextMenuListeners.remove(listener);
6827 	}
6828 
6829 	/**
6830 	 * Returns whether this editor can handle the move of the original element
6831 	 * so that it ends up being the moved element. By default this method
6832 	 * returns <code>true</code>. Subclasses may reimplement.
6833 	 *
6834 	 * @param originalElement the original element
6835 	 * @param movedElement the moved element
6836 	 * @return whether this editor can handle the move of the original element
6837 	 *         so that it ends up being the moved element
6838 	 * @since 2.0
6839 	 */
canHandleMove(IEditorInput originalElement, IEditorInput movedElement)6840 	protected boolean canHandleMove(IEditorInput originalElement, IEditorInput movedElement) {
6841 		return true;
6842 	}
6843 
6844 	/**
6845 	 * Returns the offset of the given source viewer's document that corresponds
6846 	 * to the given widget offset or <code>-1</code> if there is no such offset.
6847 	 *
6848 	 * @param viewer the source viewer
6849 	 * @param widgetOffset the widget offset
6850 	 * @return the corresponding offset in the source viewer's document or <code>-1</code>
6851 	 * @since 2.1
6852 	 */
widgetOffset2ModelOffset(ISourceViewer viewer, int widgetOffset)6853 	protected static final int widgetOffset2ModelOffset(ISourceViewer viewer, int widgetOffset) {
6854 		if (viewer instanceof ITextViewerExtension5) {
6855 			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
6856 			return extension.widgetOffset2ModelOffset(widgetOffset);
6857 		}
6858 		return widgetOffset + viewer.getVisibleRegion().getOffset();
6859 	}
6860 
6861 	/**
6862 	 * Returns the offset of the given source viewer's text widget that corresponds
6863 	 * to the given model offset or <code>-1</code> if there is no such offset.
6864 	 *
6865 	 * @param viewer the source viewer
6866 	 * @param modelOffset the model offset
6867 	 * @return the corresponding offset in the source viewer's text widget or <code>-1</code>
6868 	 * @since 3.0
6869 	 */
modelOffset2WidgetOffset(ISourceViewer viewer, int modelOffset)6870 	protected static final int modelOffset2WidgetOffset(ISourceViewer viewer, int modelOffset) {
6871 		if (viewer instanceof ITextViewerExtension5) {
6872 			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
6873 			return extension.modelOffset2WidgetOffset(modelOffset);
6874 		}
6875 		return modelOffset - viewer.getVisibleRegion().getOffset();
6876 	}
6877 
6878 	/**
6879 	 * Returns the minimal region of the given source viewer's document that completely
6880 	 * comprises everything that is visible in the viewer's widget.
6881 	 *
6882 	 * @param viewer the viewer go return the coverage for
6883 	 * @return the minimal region of the source viewer's document comprising the contents of the viewer's widget
6884 	 * @since 2.1
6885 	 */
getCoverage(ISourceViewer viewer)6886 	protected static final IRegion getCoverage(ISourceViewer viewer) {
6887 		if (viewer instanceof ITextViewerExtension5) {
6888 			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
6889 			return extension.getModelCoverage();
6890 		}
6891 		return viewer.getVisibleRegion();
6892 	}
6893 
6894 	/**
6895 	 * Tells whether the given region is visible in the given source viewer.
6896 	 *
6897 	 * @param viewer the source viewer
6898 	 * @param offset the offset of the region
6899 	 * @param length the length of the region
6900 	 * @return <code>true</code> if visible
6901 	 * @since 2.1
6902 	 */
isVisible(ISourceViewer viewer, int offset, int length)6903 	protected static final boolean isVisible(ISourceViewer viewer, int offset, int length) {
6904 		if (viewer instanceof ITextViewerExtension5) {
6905 			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
6906 			IRegion overlap= extension.modelRange2WidgetRange(new Region(offset, length));
6907 			return overlap != null;
6908 		}
6909 		return viewer.overlapsWithVisibleRegion(offset, length);
6910 	}
6911 
6912 	@Override
showChangeInformation(boolean show)6913 	public void showChangeInformation(boolean show) {
6914 		// do nothing
6915 	}
6916 
6917 	@Override
isChangeInformationShowing()6918 	public boolean isChangeInformationShowing() {
6919 		return false;
6920 	}
6921 
6922 	/**
6923 	 * Sets the given message as error message to this editor's status line.
6924 	 *
6925 	 * @param message message to be set
6926 	 * @since 3.2
6927 	 */
setStatusLineErrorMessage(String message)6928 	protected void setStatusLineErrorMessage(String message) {
6929 		IEditorStatusLine statusLine= getAdapter(IEditorStatusLine.class);
6930 		if (statusLine != null)
6931 			statusLine.setMessage(true, message, null);
6932 	}
6933 
6934 	/**
6935 	 * Sets the given message as message to this editor's status line.
6936 	 *
6937 	 * @param message message to be set
6938 	 * @since 3.2
6939 	 */
setStatusLineMessage(String message)6940 	protected void setStatusLineMessage(String message) {
6941 		IEditorStatusLine statusLine= getAdapter(IEditorStatusLine.class);
6942 		if (statusLine != null)
6943 			statusLine.setMessage(false, message, null);
6944 	}
6945 
6946 	/**
6947 	 * Jumps to the next annotation according to the given direction.
6948 	 *
6949 	 * @param forward <code>true</code> if search direction is forward, <code>false</code> if backward
6950 	 * @return the selected annotation or <code>null</code> if none
6951 	 * @see #isNavigationTarget(Annotation)
6952 	 * @see #findAnnotation(int, int, boolean, Position)
6953 	 * @since 3.2
6954 	 */
6955 	@Override
gotoAnnotation(boolean forward)6956 	public Annotation gotoAnnotation(boolean forward) {
6957 		ITextSelection selection= (ITextSelection) getSelectionProvider().getSelection();
6958 		Position position= new Position(0, 0);
6959 		Annotation annotation= findAnnotation(selection.getOffset(), selection.getLength(), forward, position);
6960 		setStatusLineErrorMessage(null);
6961 		setStatusLineMessage(null);
6962 
6963 		if (annotation != null) {
6964 			selectAndReveal(position.getOffset(), position.getLength());
6965 			setStatusLineMessage(annotation.getText());
6966 		}
6967 		return annotation;
6968 	}
6969 
6970 	/**
6971 	 * Returns the annotation closest to the given range respecting the given
6972 	 * direction. If an annotation is found, the annotations current position
6973 	 * is copied into the provided annotation position.
6974 	 *
6975 	 * @param offset the region offset
6976 	 * @param length the region length
6977 	 * @param forward <code>true</code> for forwards, <code>false</code> for backward
6978 	 * @param annotationPosition the position of the found annotation
6979 	 * @return the found annotation
6980 	 * @since 3.2
6981 	 */
findAnnotation(final int offset, final int length, boolean forward, Position annotationPosition)6982 	protected Annotation findAnnotation(final int offset, final int length, boolean forward, Position annotationPosition) {
6983 
6984 		Annotation nextAnnotation= null;
6985 		Position nextAnnotationPosition= null;
6986 		Annotation containingAnnotation= null;
6987 		Position containingAnnotationPosition= null;
6988 		boolean currentAnnotation= false;
6989 
6990 		IDocument document= getDocumentProvider().getDocument(getEditorInput());
6991 		int endOfDocument = 0;
6992 		if (document != null) {
6993 			endOfDocument = document.getLength();
6994 		}
6995 		int distance= Integer.MAX_VALUE;
6996 
6997 		IAnnotationModel model= getDocumentProvider().getAnnotationModel(getEditorInput());
6998 		Iterator<Annotation> e= model.getAnnotationIterator();
6999 		while (e.hasNext()) {
7000 			Annotation a= e.next();
7001 			if (!isNavigationTarget(a))
7002 				continue;
7003 
7004 			Position p= model.getPosition(a);
7005 			if (p == null)
7006 				continue;
7007 
7008 			if (forward && p.offset == offset || !forward && p.offset + p.getLength() == offset + length) {// || p.includes(offset)) {
7009 				if (containingAnnotation == null || (forward && p.length >= containingAnnotationPosition.length || !forward && p.length >= containingAnnotationPosition.length)) {
7010 					containingAnnotation= a;
7011 					containingAnnotationPosition= p;
7012 					currentAnnotation= p.length == length;
7013 				}
7014 			} else {
7015 				int currentDistance= 0;
7016 
7017 				if (forward) {
7018 					currentDistance= p.getOffset() - offset;
7019 					if (currentDistance < 0)
7020 						currentDistance= endOfDocument + currentDistance;
7021 
7022 					if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
7023 						distance= currentDistance;
7024 						nextAnnotation= a;
7025 						nextAnnotationPosition= p;
7026 					}
7027 				} else {
7028 					currentDistance= offset + length - (p.getOffset() + p.length);
7029 					if (currentDistance < 0)
7030 						currentDistance= endOfDocument + currentDistance;
7031 
7032 					if (currentDistance < distance || currentDistance == distance && p.length < nextAnnotationPosition.length) {
7033 						distance= currentDistance;
7034 						nextAnnotation= a;
7035 						nextAnnotationPosition= p;
7036 					}
7037 				}
7038 			}
7039 		}
7040 		if (containingAnnotationPosition != null && (!currentAnnotation || nextAnnotation == null)) {
7041 			annotationPosition.setOffset(containingAnnotationPosition.getOffset());
7042 			annotationPosition.setLength(containingAnnotationPosition.getLength());
7043 			return containingAnnotation;
7044 		}
7045 		if (nextAnnotationPosition != null) {
7046 			annotationPosition.setOffset(nextAnnotationPosition.getOffset());
7047 			annotationPosition.setLength(nextAnnotationPosition.getLength());
7048 		}
7049 
7050 		return nextAnnotation;
7051 	}
7052 
7053 	/**
7054 	 * Returns whether the given annotation is configured as a target for the
7055 	 * "Go to Next/Previous Annotation" actions.
7056 	 * <p>
7057 	 * Per default every annotation is a target.
7058 	 * </p>
7059 	 *
7060 	 * @param annotation the annotation
7061 	 * @return <code>true</code> if this is a target, <code>false</code> otherwise
7062 	 * @since 3.2
7063 	 */
isNavigationTarget(Annotation annotation)7064 	protected boolean isNavigationTarget(Annotation annotation) {
7065 		return true;
7066 	}
7067 
7068 	@Override
showRevisionInformation(RevisionInformation info, String quickDiffProviderId)7069 	public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
7070 		// no implementation
7071 	}
7072 
7073 	@Override
restoreState(IMemento memento)7074 	public void restoreState(IMemento memento) {
7075 		fMementoToRestore= memento;
7076 	}
7077 
7078 	@Override
saveState(IMemento memento)7079 	public void saveState(IMemento memento) {
7080 		ISelection selection= doGetSelection();
7081 		if (selection instanceof ITextSelection) {
7082 			memento.putInteger(TAG_SELECTION_OFFSET, ((ITextSelection)selection).getOffset());
7083 			memento.putInteger(TAG_SELECTION_LENGTH, ((ITextSelection)selection).getLength());
7084 		}
7085 		final StyledText textWidget= fSourceViewer.getTextWidget();
7086 		memento.putInteger(TAG_SELECTION_TOP_PIXEL, textWidget.getTopPixel());
7087 		memento.putInteger(TAG_SELECTION_HORIZONTAL_PIXEL, textWidget.getHorizontalPixel());
7088 	}
7089 
7090 	/**
7091 	 * Returns whether the given memento contains saved state
7092 	 * <p>
7093 	 * Subclasses may extend or override this method.</p>
7094 	 *
7095 	 * @param memento the saved state of this editor
7096 	 * @return <code>true</code> if the given memento contains saved state
7097 	 * @since 3.3
7098 	 */
containsSavedState(IMemento memento)7099 	protected boolean containsSavedState(IMemento memento) {
7100 		return memento.getInteger(TAG_SELECTION_OFFSET) != null && memento.getInteger(TAG_SELECTION_LENGTH) != null;
7101 	}
7102 
7103 	/**
7104 	 * Restores this editor's state using the given memento.
7105 	 * <p>
7106 	 * Subclasses may extend or override this method.</p>
7107 	 *
7108 	 * @param memento the saved state of this editor
7109 	 * @since 3.3
7110 	 */
doRestoreState(IMemento memento)7111 	protected void doRestoreState(IMemento memento) {
7112 		Integer offset= memento.getInteger(TAG_SELECTION_OFFSET);
7113 		if (offset == null)
7114 			return;
7115 
7116 		Integer length= memento.getInteger(TAG_SELECTION_LENGTH);
7117 		if (length == null)
7118 			return;
7119 
7120 		doSetSelection(new TextSelection(offset.intValue(), length.intValue()));
7121 
7122 		final StyledText textWidget= fSourceViewer.getTextWidget();
7123 
7124 		Integer topPixel= memento.getInteger(TAG_SELECTION_TOP_PIXEL);
7125 		if (topPixel != null)
7126 			textWidget.setTopPixel(topPixel.intValue());
7127 
7128 		Integer horizontalPixel= memento.getInteger(TAG_SELECTION_HORIZONTAL_PIXEL);
7129 		if (horizontalPixel != null)
7130 			textWidget.setHorizontalPixel(horizontalPixel.intValue());
7131 	}
7132 
7133 	@Override
getSaveables()7134 	public Saveable[] getSaveables() {
7135 		if (fSavable == null)
7136 			fSavable= new TextEditorSavable(this);
7137 
7138 		return new Saveable[] { fSavable };
7139 	}
7140 
7141 	@Override
getActiveSaveables()7142 	public Saveable[] getActiveSaveables() {
7143 		return getSaveables();
7144 	}
7145 
7146 	/**
7147 	 * This text editor's savable.
7148 	 *
7149 	 * @since 3.3
7150 	 */
7151 	protected static class TextEditorSavable extends Saveable {
7152 
7153 		/** The cached editor. */
7154 		private ITextEditor fTextEditor;
7155 		/** The cached editor input. */
7156 		private IEditorInput fEditorInput;
7157 		/** The cached document. */
7158 		private IDocument fDocument;
7159 
7160 		/**
7161 		 * Creates a new savable for this text editor.
7162 		 *
7163 		 * @param textEditor the text editor
7164 		 */
TextEditorSavable(ITextEditor textEditor)7165 		public TextEditorSavable(ITextEditor textEditor) {
7166 			Assert.isLegal(textEditor != null);
7167 			fTextEditor= textEditor;
7168 			fEditorInput= fTextEditor.getEditorInput();
7169 			Assert.isLegal(fEditorInput != null);
7170 		}
7171 
7172 		/**
7173 		 * Disconnects the editor from this savable.
7174 		 */
disconnectEditor()7175 		public void disconnectEditor() {
7176 			getAdapter(IDocument.class); // make sure the document is cached
7177 			fTextEditor= null;
7178 		}
7179 
7180 		@Override
getName()7181 		public String getName() {
7182 			return fEditorInput.getName();
7183 		}
7184 
7185 		@Override
getToolTipText()7186 		public String getToolTipText() {
7187 			return fEditorInput.getToolTipText();
7188 		}
7189 
7190 		@Override
getImageDescriptor()7191 		public ImageDescriptor getImageDescriptor() {
7192 			return fEditorInput.getImageDescriptor();
7193 		}
7194 
7195 		@Override
doSave(IProgressMonitor monitor)7196 		public void doSave(IProgressMonitor monitor) throws CoreException {
7197 			try {
7198 				fTextEditor.doSave(monitor);
7199 			} catch (NullPointerException e) {
7200 				// This should not happen. Code added to handle the below bug.
7201 				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=550336
7202 				Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
7203 				ILog log = Platform.getLog(bundle);
7204 				Status status = new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, null, e);
7205 				log.log(status);
7206 			}
7207 		}
7208 
7209 		@Override
isDirty()7210 		public boolean isDirty() {
7211 			try {
7212 				return fTextEditor.isDirty();
7213 			} catch (NullPointerException e) {
7214 				// This should not happen. Code added to handle the below bug.
7215 				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=550336
7216 				Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
7217 				ILog log = Platform.getLog(bundle);
7218 				Status status = new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, null, e);
7219 				log.log(status);
7220 				return false;
7221 			}
7222 		}
7223 
7224 		/*
7225 		 * @see org.eclipse.ui.Saveable#supportsBackgroundSave()
7226 		 */
supportsBackgroundSave()7227 		public boolean supportsBackgroundSave() {
7228 			return false;
7229 		}
7230 
7231 		@Override
hashCode()7232 		public int hashCode() {
7233 			Object document= getAdapter(IDocument.class);
7234 			if (document == null)
7235 				return 0;
7236 			return document.hashCode();
7237 		}
7238 
7239 		@Override
equals(Object obj)7240 		public boolean equals(Object obj) {
7241 			if (this == obj)
7242 				return true;
7243 
7244 			if (!(obj instanceof Saveable))
7245 				return false;
7246 
7247 			Object thisDocument= getAdapter(IDocument.class);
7248 			Object otherDocument= ((Saveable)obj).getAdapter(IDocument.class);
7249 
7250 			if (thisDocument == null && otherDocument == null)
7251 				return true;
7252 
7253 			return thisDocument != null && thisDocument.equals(otherDocument);
7254 		}
7255 
7256 		/**
7257 		 * Explicit comment needed to suppress wrong warning caused by
7258 		 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4848177
7259 		 *
7260 		 * @see org.eclipse.ui.Saveable#getAdapter(java.lang.Class)
7261 		 */
7262 		@SuppressWarnings("unchecked")
7263 		@Override
getAdapter(Class<T> adapter)7264 		public <T> T getAdapter(Class<T> adapter) {
7265 			if (adapter == IDocument.class) {
7266 				if (fDocument == null) {
7267 					IDocumentProvider documentProvider= fTextEditor.getDocumentProvider();
7268 					if (documentProvider != null)
7269 						fDocument= documentProvider.getDocument(fEditorInput);
7270 				}
7271 				return (T) fDocument;
7272 			}
7273 			return super.getAdapter(adapter);
7274 		}
7275 	}
7276 
7277 	//---- Tabs to spaces conversion support ------------------
7278 
7279 	/**
7280 	 * Installs a tabs to spaces converter.
7281 	 *
7282 	 * <p>Subclasses may extend or override this method.</p>
7283 	 *
7284 	 * @since 3.3
7285 	 */
installTabsToSpacesConverter()7286 	protected void installTabsToSpacesConverter() {
7287 		SourceViewerConfiguration config= getSourceViewerConfiguration();
7288 		if (config != null && fSourceViewer instanceof ITextViewerExtension7) {
7289 			int tabWidth= config.getTabWidth(fSourceViewer);
7290 			TabsToSpacesConverter tabToSpacesConverter= new TabsToSpacesConverter();
7291 			tabToSpacesConverter.setLineTracker(new DefaultLineTracker());
7292 			tabToSpacesConverter.setNumberOfSpacesPerTab(tabWidth);
7293 			tabToSpacesConverter.setDeleteSpacesAsTab(isSpacesAsTabsDeletionEnabled());
7294 			((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(tabToSpacesConverter);
7295 			updateIndentPrefixes();
7296 		}
7297 	}
7298 
7299 	/**
7300 	 * Installs a tabs to spaces converter.
7301 	 *
7302 	 * <p>Subclasses may extend or override this method.</p>
7303 	 *
7304 	 * @since 3.3
7305 	 */
uninstallTabsToSpacesConverter()7306 	protected void uninstallTabsToSpacesConverter() {
7307 		if (fSourceViewer instanceof ITextViewerExtension7) {
7308 			((ITextViewerExtension7)fSourceViewer).setTabsToSpacesConverter(null);
7309 			if (fSourceViewer.getTextWidget() != null)
7310 				updateIndentPrefixes();
7311 		}
7312 	}
7313 
7314 	/**
7315 	 * Tells whether tabs should be converted to
7316 	 * spaces while editing inside this editor.
7317 	 *
7318 	 * <p>Subclasses may override this method.</p>
7319 	 *
7320 	 * @return <code>true</code> if tabs should be converted to spaces
7321 	 * @since 3.3
7322 	 */
isTabsToSpacesConversionEnabled()7323 	protected boolean isTabsToSpacesConversionEnabled() {
7324 		return false;
7325 	}
7326 
7327 	/**
7328 	 * Tells whether delete and backspace keys should remove multiple spaces as
7329 	 * if they were a tab. Only relevant when
7330 	 * {@link #isTabsToSpacesConversionEnabled()} returns true.
7331 	 *
7332 	 * <p>
7333 	 * Subclasses may override this method.
7334 	 * </p>
7335 	 *
7336 	 * @return <code>true</code> if spaces should be removed as tabs
7337 	 * @since 3.14
7338 	 */
isSpacesAsTabsDeletionEnabled()7339 	protected boolean isSpacesAsTabsDeletionEnabled() {
7340 		return false;
7341 	}
7342 
7343 	/**
7344 	 * Updates the source viewer's indent prefixes with
7345 	 * the values provided by the source viewer configuration.
7346 	 *
7347 	 * @since 3.3
7348 	 */
updateIndentPrefixes()7349 	protected final void updateIndentPrefixes() {
7350 		SourceViewerConfiguration configuration= getSourceViewerConfiguration();
7351 		String[] types= configuration.getConfiguredContentTypes(fSourceViewer);
7352 		for (int i= 0; i < types.length; i++) {
7353 			String[] prefixes= configuration.getIndentPrefixes(fSourceViewer, types[i]);
7354 			if (prefixes != null && prefixes.length > 0)
7355 				fSourceViewer.setIndentPrefixes(prefixes, types[i]);
7356 		}
7357 	}
7358 
7359 	/**
7360 	 * Tells whether selection mode is supported.
7361 	 * <p>
7362 	 * By default block selection mode is supported. Subclasses may override this method to disable
7363 	 * it.
7364 	 * </p>
7365 	 *
7366 	 * @return <code>true</code> if block selection mode is supported, <code>false</code> otherwise
7367 	 * @since 3.5
7368 	 */
isBlockSelectionModeSupported()7369 	protected boolean isBlockSelectionModeSupported() {
7370 		return true;
7371 	}
7372 
7373 	/**
7374 	 * @see org.eclipse.ui.texteditor.ITextEditorExtension5#isBlockSelectionModeEnabled()
7375 	 * @since 3.5
7376 	 */
7377 	@Override
isBlockSelectionModeEnabled()7378 	public final boolean isBlockSelectionModeEnabled() {
7379 		ISourceViewer viewer= getSourceViewer();
7380 		if (viewer != null) {
7381 			StyledText styledText= viewer.getTextWidget();
7382 			if (styledText != null)
7383 				return styledText.getBlockSelection();
7384 		}
7385 		return false;
7386 	}
7387 
7388 	/**
7389 	 * @see org.eclipse.ui.texteditor.ITextEditorExtension5#setBlockSelectionMode(boolean)
7390 	 * @since 3.5
7391 	 */
7392 	@Override
setBlockSelectionMode(boolean enable)7393 	public void setBlockSelectionMode(boolean enable) {
7394 		if (!isBlockSelectionModeSupported())
7395 			return;
7396 
7397 		ISourceViewer viewer= getSourceViewer();
7398 		if (viewer != null) {
7399 			StyledText styledText= viewer.getTextWidget();
7400 			if (styledText != null) {
7401 				/*
7402 				 * Font switching. block selection mode needs a monospace font.
7403 				 *  - set the font _before enabling_ block selection mode in order to maintain the
7404 				 * selection
7405 				 * - revert the font _after disabling_ block selection mode in order to maintain the
7406 				 * selection
7407 				 */
7408 				if (enable) {
7409 					Font blockFont= JFaceResources.getFont(BLOCK_SELECTION_MODE_FONT);
7410 					Font normalFont= styledText.getFont();
7411 					if (!blockFont.equals(normalFont) && !normalFont.getFontData()[0].equals(blockFont.getFontData()[0])) {
7412 						setFont(viewer, blockFont);
7413 						disposeFont();
7414 						updateCaret();
7415 					}
7416 
7417 					// we must unset word wrap before we can set block selection
7418 					if (isWordWrapEnabled()) {
7419 						setWordWrap(false);
7420 					}
7421 				}
7422 
7423 				styledText.setBlockSelection(enable);
7424 
7425 				if (!enable) {
7426 					initializeViewerFont(viewer);
7427 					updateCaret();
7428 				}
7429 			}
7430 		}
7431 	}
7432 
7433 	/**
7434 	 * Tells whether word wrap is supported.
7435 	 * <p>
7436 	 * By default word wrap is supported. Subclasses may override this method to disable
7437 	 * it.
7438 	 * </p>
7439 	 *
7440 	 * @return <code>true</code> if word wrap is supported, <code>false</code> otherwise
7441 	 * @since 3.10
7442 	 */
isWordWrapSupported()7443 	protected boolean isWordWrapSupported() {
7444 		return true;
7445 	}
7446 
7447 	/**
7448 	 * <code>true</code> if word wrap is supported and enabled, <code>false</code> otherwise
7449 	 * @return the receiver's word wrap state if word wrap is supported
7450 	 * @since 3.10
7451 	 * @see AbstractTextEditor#isWordWrapSupported()
7452 	 */
7453 	@Override
isWordWrapEnabled()7454 	public final boolean isWordWrapEnabled() {
7455 		if (!isWordWrapSupported()) {
7456 			return false;
7457 		}
7458 		ISourceViewer viewer= getSourceViewer();
7459 		if (viewer != null) {
7460 			StyledText styledText= viewer.getTextWidget();
7461 			if (styledText != null) {
7462 				return styledText.getWordWrap();
7463 			}
7464 		}
7465 		return false;
7466 	}
7467 
7468 	/**
7469 	 * @see org.eclipse.ui.texteditor.ITextEditorExtension6#setWordWrap(boolean)
7470 	 * @since 3.10
7471 	 */
7472 	@Override
setWordWrap(boolean enable)7473 	public void setWordWrap(boolean enable) {
7474 		if (!isWordWrapSupported() || isWordWrapEnabled() == enable) {
7475 			return;
7476 		}
7477 
7478 		ISourceViewer viewer= getSourceViewer();
7479 		if (viewer != null) {
7480 			StyledText styledText= viewer.getTextWidget();
7481 			if (styledText != null) {
7482 				if (isBlockSelectionModeEnabled()) {
7483 					setBlockSelectionMode(false);
7484 				}
7485 				styledText.setWordWrap(enable);
7486 				if (fVerticalRuler != null) {
7487 					// update ruler layout so that it can consider
7488 					// changed horizontal scrollbar visibility
7489 					boolean updated= false;
7490 					if (viewer instanceof ITextViewerExtension) {
7491 						Control control= ((ITextViewerExtension)viewer).getControl();
7492 						if (control instanceof Composite) {
7493 							((Composite)control).layout();
7494 							updated= true;
7495 						}
7496 					}
7497 					if (!updated) {
7498 						fVerticalRuler.update();
7499 					}
7500 				}
7501 			}
7502 		}
7503 	}
7504 
7505 	/**
7506 	 * Returns the initial word wrap status.
7507 	 *
7508 	 * @return initial word wrap status
7509 	 * @since 3.10
7510 	 */
getInitialWordWrapStatus()7511 	protected boolean getInitialWordWrapStatus() {
7512 		IPreferenceStore store= getPreferenceStore();
7513 		if (store == null) {
7514 			return false;
7515 		}
7516 		return store.getBoolean(PREFERENCE_WORD_WRAP_ENABLED);
7517 	}
7518 }
7519