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 < 0, it's scrolling up, if > 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