| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Max Weninger (max.weninger@windriver.com) - Bug 131895 [Edit] Undo in compare |
| * Max Weninger (max.weninger@windriver.com) - Bug 72936 [Viewers] Show line numbers in comparision |
| *******************************************************************************/ |
| package org.eclipse.compare.internal; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ResourceBundle; |
| |
| import org.eclipse.compare.ICompareContainer; |
| import org.eclipse.core.commands.Command; |
| import org.eclipse.core.commands.IHandler; |
| import org.eclipse.core.commands.operations.IOperationHistory; |
| import org.eclipse.core.commands.operations.IOperationHistoryListener; |
| import org.eclipse.core.commands.operations.IUndoContext; |
| import org.eclipse.core.commands.operations.OperationHistoryEvent; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.action.GroupMarker; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.preference.PreferenceConverter; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ITextListener; |
| import org.eclipse.jface.text.ITextOperationTarget; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.ITextViewerExtension5; |
| import org.eclipse.jface.text.IUndoManager; |
| import org.eclipse.jface.text.IUndoManagerExtension; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextEvent; |
| import org.eclipse.jface.text.source.IAnnotationModel; |
| import org.eclipse.jface.text.source.ISharedTextColors; |
| import org.eclipse.jface.text.source.LineNumberRulerColumn; |
| import org.eclipse.jface.text.source.SourceViewer; |
| import org.eclipse.jface.text.source.SourceViewerConfiguration; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IPropertyListener; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| import org.eclipse.ui.IWorkbenchCommandConstants; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.editors.text.EditorsUI; |
| import org.eclipse.ui.menus.CommandContributionItem; |
| import org.eclipse.ui.menus.CommandContributionItemParameter; |
| import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; |
| import org.eclipse.ui.texteditor.ChangeEncodingAction; |
| import org.eclipse.ui.texteditor.FindReplaceAction; |
| import org.eclipse.ui.texteditor.IDocumentProvider; |
| import org.eclipse.ui.texteditor.IElementStateListener; |
| import org.eclipse.ui.texteditor.ITextEditor; |
| |
| /** |
| * Wraps a JFace SourceViewer and add some convenience methods. |
| */ |
| public class MergeSourceViewer implements ISelectionChangedListener, |
| ITextListener, IMenuListener, IOperationHistoryListener, IAdaptable { |
| |
| public static final String UNDO_ID= "undo"; //$NON-NLS-1$ |
| public static final String REDO_ID= "redo"; //$NON-NLS-1$ |
| public static final String CUT_ID= "cut"; //$NON-NLS-1$ |
| public static final String COPY_ID= "copy"; //$NON-NLS-1$ |
| public static final String PASTE_ID= "paste"; //$NON-NLS-1$ |
| public static final String DELETE_ID= "delete"; //$NON-NLS-1$ |
| public static final String SELECT_ALL_ID= "selectAll"; //$NON-NLS-1$ |
| public static final String FIND_ID= "find"; //$NON-NLS-1$ |
| public static final String GOTO_LINE_ID= "gotoLine"; //$NON-NLS-1$ |
| public static final String CHANGE_ENCODING_ID= "changeEncoding"; //$NON-NLS-1$ |
| |
| class TextOperationAction extends MergeViewerAction { |
| |
| private int fOperationCode; |
| |
| TextOperationAction(int operationCode, boolean mutable, boolean selection, boolean content) { |
| this(operationCode, null, mutable, selection, content); |
| } |
| |
| public TextOperationAction(int operationCode, String actionDefinitionId, boolean mutable, boolean selection, boolean content) { |
| super(mutable, selection, content); |
| if (actionDefinitionId != null) |
| setActionDefinitionId(actionDefinitionId); |
| fOperationCode= operationCode; |
| update(); |
| } |
| |
| @Override |
| public void run() { |
| if (isEnabled()) |
| getSourceViewer().doOperation(fOperationCode); |
| } |
| |
| @Override |
| public boolean isEnabled() { |
| return fOperationCode != -1 && getSourceViewer().canDoOperation(fOperationCode); |
| } |
| |
| @Override |
| public void update() { |
| setEnabled(isEnabled()); |
| } |
| } |
| |
| /** |
| * TODO: The only purpose of this class is to provide "Go to Line" action in |
| * TextMergeViewer. The adapter should be removed as soon as we implement |
| * embedded TextEditor in a similar way JDT has it done for Java compare. |
| */ |
| class TextEditorAdapter implements ITextEditor { |
| |
| @Override |
| public void close(boolean save) { |
| // defining interface method |
| } |
| |
| @Override |
| public void doRevertToSaved() { |
| // defining interface method |
| } |
| |
| @Override |
| public IAction getAction(String actionId) { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public IDocumentProvider getDocumentProvider() { |
| return new IDocumentProvider(){ |
| |
| @Override |
| public void aboutToChange(Object element) { |
| // defining interface method |
| } |
| |
| @Override |
| public void addElementStateListener( |
| IElementStateListener listener) { |
| // defining interface method |
| } |
| |
| @Override |
| public boolean canSaveDocument(Object element) { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public void changed(Object element) { |
| // defining interface method |
| } |
| |
| @Override |
| public void connect(Object element) throws CoreException { |
| // defining interface method |
| } |
| |
| @Override |
| public void disconnect(Object element) { |
| // defining interface method |
| } |
| |
| @Override |
| public IAnnotationModel getAnnotationModel(Object element) { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public IDocument getDocument(Object element) { |
| return MergeSourceViewer.this.getSourceViewer().getDocument(); |
| } |
| |
| @Override |
| public long getModificationStamp(Object element) { |
| // defining interface method |
| return 0; |
| } |
| |
| @Override |
| public long getSynchronizationStamp(Object element) { |
| // defining interface method |
| return 0; |
| } |
| |
| @Override |
| public boolean isDeleted(Object element) { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public boolean mustSaveDocument(Object element) { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public void removeElementStateListener( |
| IElementStateListener listener) { |
| // defining interface method |
| } |
| |
| @Override |
| public void resetDocument(Object element) throws CoreException { |
| // defining interface method |
| } |
| |
| @Override |
| public void saveDocument(IProgressMonitor monitor, |
| Object element, IDocument document, boolean overwrite) |
| throws CoreException { |
| // defining interface method |
| }}; |
| } |
| |
| @Override |
| public IRegion getHighlightRange() { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public ISelectionProvider getSelectionProvider() { |
| return MergeSourceViewer.this.getSourceViewer().getSelectionProvider(); |
| } |
| |
| @Override |
| public boolean isEditable() { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public void removeActionActivationCode(String actionId) { |
| // defining interface method |
| } |
| |
| @Override |
| public void resetHighlightRange() { |
| // defining interface method |
| } |
| |
| @Override |
| public void selectAndReveal(int start, int length) { |
| selectAndReveal(start, length, start, length); |
| } |
| |
| private void selectAndReveal(int selectionStart, int selectionLength, int revealStart, int revealLength) { |
| |
| ISelection selection = getSelectionProvider().getSelection(); |
| if (selection instanceof ITextSelection) { |
| ITextSelection textSelection = (ITextSelection) selection; |
| if (textSelection.getOffset() != 0 || textSelection.getLength() != 0) |
| markInNavigationHistory(); |
| } |
| |
| StyledText widget= MergeSourceViewer.this.getSourceViewer().getTextWidget(); |
| widget.setRedraw(false); |
| { |
| adjustHighlightRange(revealStart, revealLength); |
| MergeSourceViewer.this.getSourceViewer().revealRange(revealStart, revealLength); |
| |
| MergeSourceViewer.this.getSourceViewer().setSelectedRange(selectionStart, selectionLength); |
| |
| markInNavigationHistory(); |
| } |
| widget.setRedraw(true); |
| } |
| |
| private void markInNavigationHistory() { |
| getSite().getPage().getNavigationHistory().markLocation(this); |
| } |
| |
| private void adjustHighlightRange(int offset, int length) { |
| |
| if (MergeSourceViewer.this instanceof ITextViewerExtension5) { |
| ITextViewerExtension5 extension= (ITextViewerExtension5) MergeSourceViewer.this; |
| extension.exposeModelRange(new Region(offset, length)); |
| } else if (!isVisible(MergeSourceViewer.this.getSourceViewer(), offset, length)) { |
| MergeSourceViewer.this.getSourceViewer().resetVisibleRegion(); |
| } |
| } |
| |
| private /*static*/ final boolean isVisible(ITextViewer viewer, int offset, int length) { |
| if (viewer instanceof ITextViewerExtension5) { |
| ITextViewerExtension5 extension= (ITextViewerExtension5) viewer; |
| IRegion overlap= extension.modelRange2WidgetRange(new Region(offset, length)); |
| return overlap != null; |
| } |
| return viewer.overlapsWithVisibleRegion(offset, length); |
| } |
| |
| @Override |
| public void setAction(String actionID, IAction action) { |
| // defining interface method |
| } |
| |
| @Override |
| public void setActionActivationCode(String actionId, |
| char activationCharacter, int activationKeyCode, |
| int activationStateMask) { |
| // defining interface method |
| } |
| |
| @Override |
| public void setHighlightRange(int offset, int length, boolean moveCursor) { |
| // defining interface method |
| } |
| |
| @Override |
| public void showHighlightRangeOnly(boolean showHighlightRangeOnly) { |
| // defining interface method |
| } |
| |
| @Override |
| public boolean showsHighlightRangeOnly() { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public IEditorInput getEditorInput() { |
| if (MergeSourceViewer.this.fContainer.getWorkbenchPart() instanceof IEditorPart) |
| return ((IEditorPart) MergeSourceViewer.this.fContainer.getWorkbenchPart()).getEditorInput(); |
| return null; |
| } |
| |
| @Override |
| public IEditorSite getEditorSite() { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public void init(IEditorSite site, IEditorInput input) |
| throws PartInitException { |
| // defining interface method |
| } |
| |
| @Override |
| public void addPropertyListener(IPropertyListener listener) { |
| // defining interface method |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| // defining interface method |
| } |
| |
| @Override |
| public void dispose() { |
| // defining interface method |
| } |
| |
| @Override |
| public IWorkbenchPartSite getSite() { |
| return MergeSourceViewer.this.fContainer.getWorkbenchPart().getSite(); |
| } |
| |
| @Override |
| public String getTitle() { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public Image getTitleImage() { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public String getTitleToolTip() { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public void removePropertyListener(IPropertyListener listener) { |
| // defining interface method |
| } |
| |
| @Override |
| public void setFocus() { |
| // defining interface method |
| } |
| |
| @Override |
| public <T> T getAdapter(Class<T> adapter) { |
| // defining interface method |
| return null; |
| } |
| |
| @Override |
| public void doSave(IProgressMonitor monitor) { |
| // defining interface method |
| } |
| |
| @Override |
| public void doSaveAs() { |
| // defining interface method |
| } |
| |
| @Override |
| public boolean isDirty() { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public boolean isSaveAsAllowed() { |
| // defining interface method |
| return false; |
| } |
| |
| @Override |
| public boolean isSaveOnCloseNeeded() { |
| // defining interface method |
| return false; |
| } |
| } |
| |
| private ResourceBundle fResourceBundle; |
| private ICompareContainer fContainer; |
| private SourceViewer fSourceViewer; |
| private Position fRegion; |
| private boolean fEnabled= true; |
| private HashMap<String, IAction> fActions= new HashMap<>(); |
| private IDocument fRememberedDocument; |
| |
| private boolean fAddSaveAction= true; |
| private boolean isConfigured = false; |
| |
| // line number ruler support |
| private IPropertyChangeListener fPreferenceChangeListener; |
| private boolean fShowLineNumber=false; |
| private LineNumberRulerColumn fLineNumberColumn; |
| private List<IAction> textActions = new ArrayList<>(); |
| private CommandContributionItem fSaveContributionItem; |
| |
| public MergeSourceViewer(SourceViewer sourceViewer, ResourceBundle bundle, ICompareContainer container) { |
| Assert.isNotNull(sourceViewer); |
| fSourceViewer= sourceViewer; |
| fResourceBundle= bundle; |
| fContainer = container; |
| |
| MenuManager menu= new MenuManager(); |
| menu.setRemoveAllWhenShown(true); |
| menu.addMenuListener(this); |
| StyledText te= getSourceViewer().getTextWidget(); |
| te.setMenu(menu.createContextMenu(te)); |
| fContainer.registerContextMenu(menu, getSourceViewer()); |
| |
| // for listening to editor show/hide line number preference value |
| fPreferenceChangeListener= event -> MergeSourceViewer.this.handlePropertyChangeEvent(event); |
| EditorsUI.getPreferenceStore().addPropertyChangeListener(fPreferenceChangeListener); |
| fShowLineNumber= EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER); |
| if(fShowLineNumber){ |
| updateLineNumberRuler(); |
| } |
| |
| IOperationHistory history = getHistory(); |
| if (history != null) |
| history.addOperationHistoryListener(this); |
| |
| // don't add save when in a dialog, IWorkbenchPart is null in dialog containers |
| fAddSaveAction = fContainer.getWorkbenchPart() != null; |
| } |
| |
| public void rememberDocument(IDocument doc) { |
| // if (doc != null && fRememberedDocument != null) { |
| // System.err.println("MergeSourceViewer.rememberDocument: fRememberedDocument != null: shouldn't happen"); //$NON-NLS-1$ |
| // } |
| fRememberedDocument= doc; |
| } |
| |
| public IDocument getRememberedDocument() { |
| return fRememberedDocument; |
| } |
| |
| public void hideSaveAction() { |
| fAddSaveAction= false; |
| } |
| |
| public void setFont(Font font) { |
| StyledText te= getSourceViewer().getTextWidget(); |
| if (te != null) |
| te.setFont(font); |
| if (fLineNumberColumn != null) { |
| fLineNumberColumn.setFont(font); |
| layoutViewer(); |
| } |
| } |
| |
| public void setBackgroundColor(Color color) { |
| StyledText te= getSourceViewer().getTextWidget(); |
| if (te != null) |
| te.setBackground(color); |
| if (fLineNumberColumn != null) |
| fLineNumberColumn.setBackground(color); |
| } |
| |
| public void setForegroundColor(Color color) { |
| StyledText te= getSourceViewer().getTextWidget(); |
| if (te != null) |
| te.setForeground(color); |
| } |
| |
| public void setEnabled(boolean enabled) { |
| if (enabled != fEnabled) { |
| fEnabled= enabled; |
| StyledText c= getSourceViewer().getTextWidget(); |
| if (c != null) { |
| c.setEnabled(enabled); |
| Display d= c.getDisplay(); |
| if (enabled) { |
| c.setBackground(d.getSystemColor(SWT.COLOR_LIST_BACKGROUND)); |
| } |
| } |
| } |
| } |
| |
| public boolean getEnabled() { |
| return fEnabled; |
| } |
| |
| public void setRegion(Position region) { |
| fRegion= region; |
| } |
| |
| public Position getRegion() { |
| return fRegion; |
| } |
| |
| public boolean isControlOkToUse() { |
| StyledText t= getSourceViewer().getTextWidget(); |
| return t != null && !t.isDisposed(); |
| } |
| |
| public void setSelection(Position position) { |
| if (position != null) |
| getSourceViewer().setSelectedRange(position.getOffset(), position.getLength()); |
| } |
| |
| public void setLineBackground(Position position, Color c) { |
| StyledText t= getSourceViewer().getTextWidget(); |
| if (t != null && !t.isDisposed()) { |
| Point region= new Point(0, 0); |
| getLineRange(position, region); |
| |
| region.x-= getDocumentRegionOffset(); |
| |
| try { |
| t.setLineBackground(region.x, region.y, c); |
| } catch (IllegalArgumentException ex) { |
| // silently ignored |
| } |
| } |
| } |
| |
| public void resetLineBackground() { |
| StyledText t= getSourceViewer().getTextWidget(); |
| if (t != null && !t.isDisposed()) { |
| int lines= getLineCount(); |
| t.setLineBackground(0, lines, null); |
| } |
| } |
| |
| /* |
| * Returns number of lines in document region. |
| */ |
| public int getLineCount() { |
| IRegion region= getSourceViewer().getVisibleRegion(); |
| |
| int length= region.getLength(); |
| if (length == 0) |
| return 0; |
| |
| IDocument doc= getSourceViewer().getDocument(); |
| int startLine= 0; |
| int endLine= 0; |
| |
| int start= region.getOffset(); |
| try { |
| startLine= doc.getLineOfOffset(start); |
| } catch(BadLocationException ex) { |
| // silently ignored |
| } |
| try { |
| endLine= doc.getLineOfOffset(start+length); |
| } catch(BadLocationException ex) { |
| // silently ignored |
| } |
| |
| return endLine-startLine+1; |
| } |
| |
| public int getViewportLines() { |
| StyledText te= getSourceViewer().getTextWidget(); |
| Rectangle clArea= te.getClientArea(); |
| if (!clArea.isEmpty()) |
| return clArea.height / te.getLineHeight(); |
| return 0; |
| } |
| |
| public int getViewportHeight() { |
| StyledText te= getSourceViewer().getTextWidget(); |
| Rectangle clArea= te.getClientArea(); |
| if (!clArea.isEmpty()) |
| return clArea.height; |
| return 0; |
| } |
| |
| /* |
| * Returns lines |
| */ |
| public int getDocumentRegionOffset() { |
| int start= getSourceViewer().getVisibleRegion().getOffset(); |
| IDocument doc= getSourceViewer().getDocument(); |
| if (doc != null) { |
| try { |
| return doc.getLineOfOffset(start); |
| } catch(BadLocationException ex) { |
| // silently ignored |
| } |
| } |
| return 0; |
| } |
| |
| public int getVerticalScrollOffset() { |
| StyledText st= getSourceViewer().getTextWidget(); |
| int lineHeight= st.getLineHeight(); |
| return getSourceViewer().getTopInset() - ((getDocumentRegionOffset()*lineHeight) + st.getTopPixel()); |
| } |
| |
| /* |
| * Returns the start line and the number of lines which correspond to the given position. |
| * Starting line number is 0 based. |
| */ |
| public Point getLineRange(Position p, Point region) { |
| |
| IDocument doc= getSourceViewer().getDocument(); |
| |
| if (p == null || doc == null) { |
| region.x= 0; |
| region.y= 0; |
| return region; |
| } |
| |
| int start= p.getOffset(); |
| int length= p.getLength(); |
| |
| int startLine= 0; |
| try { |
| startLine= doc.getLineOfOffset(start); |
| } catch (BadLocationException e) { |
| // silently ignored |
| } |
| |
| int lineCount= 0; |
| |
| if (length == 0) { |
| // // if range length is 0 and if range starts a new line |
| // try { |
| // if (start == doc.getLineStartOffset(startLine)) { |
| // lines--; |
| // } |
| // } catch (BadLocationException e) { |
| // lines--; |
| // } |
| |
| } else { |
| int endLine= 0; |
| try { |
| endLine= doc.getLineOfOffset(start + length - 1); // why -1? |
| } catch (BadLocationException e) { |
| // silently ignored |
| } |
| lineCount= endLine-startLine+1; |
| } |
| |
| region.x= startLine; |
| region.y= lineCount; |
| return region; |
| } |
| |
| /* |
| * Scroll TextPart to the given line. |
| */ |
| public void vscroll(int line) { |
| |
| int srcViewSize= getLineCount(); |
| int srcExtentSize= getViewportLines(); |
| |
| if (srcViewSize > srcExtentSize) { |
| |
| if (line < 0) |
| line= 0; |
| |
| int cp= getSourceViewer().getTopIndex(); |
| if (cp != line) |
| getSourceViewer().setTopIndex(line + getDocumentRegionOffset()); |
| } |
| } |
| |
| public void addAction(String actionId, MergeViewerAction action) { |
| fActions.put(actionId, action); |
| } |
| |
| public IAction getAction(String actionId) { |
| IAction action= fActions.get(actionId); |
| if (action == null) { |
| action= createAction(actionId); |
| if (action == null) |
| return null; |
| if (action instanceof MergeViewerAction) { |
| MergeViewerAction mva = (MergeViewerAction) action; |
| if (mva.isContentDependent()) |
| getSourceViewer().addTextListener(this); |
| if (mva.isSelectionDependent()) |
| getSourceViewer().addSelectionChangedListener(this); |
| |
| Utilities.initAction(action, fResourceBundle, "action." + actionId + "."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| addAction(actionId, action); |
| |
| } |
| if (action instanceof MergeViewerAction) { |
| MergeViewerAction mva = (MergeViewerAction) action; |
| if (mva.isEditableDependent() && !getSourceViewer().isEditable()) |
| return null; |
| } |
| return action; |
| } |
| |
| protected IAction createAction(String actionId) { |
| if (UNDO_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.UNDO, IWorkbenchCommandConstants.EDIT_UNDO, true, false, true); |
| if (REDO_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.REDO, IWorkbenchCommandConstants.EDIT_REDO, true, false, true); |
| if (CUT_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.CUT, IWorkbenchCommandConstants.EDIT_CUT, true, true, false); |
| if (COPY_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.COPY, IWorkbenchCommandConstants.EDIT_COPY, false, true, false); |
| if (PASTE_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.PASTE, IWorkbenchCommandConstants.EDIT_PASTE, true, false, false); |
| if (DELETE_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.DELETE, IWorkbenchCommandConstants.EDIT_DELETE, true, false, false); |
| if (SELECT_ALL_ID.equals(actionId)) |
| return new TextOperationAction(ITextOperationTarget.SELECT_ALL, IWorkbenchCommandConstants.EDIT_SELECT_ALL, false, false, false); |
| return null; |
| } |
| |
| @Override |
| public void selectionChanged(SelectionChangedEvent event) { |
| Iterator<IAction> e= fActions.values().iterator(); |
| while (e.hasNext()) { |
| IAction next = e.next(); |
| if (next instanceof MergeViewerAction) { |
| MergeViewerAction action = (MergeViewerAction) next; |
| if (action.isSelectionDependent()) |
| action.update(); |
| } |
| } |
| } |
| |
| @Override |
| public void textChanged(TextEvent event) { |
| updateContentDependantActions(); |
| } |
| |
| void updateContentDependantActions() { |
| Iterator<IAction> e= fActions.values().iterator(); |
| while (e.hasNext()) { |
| IAction next = e.next(); |
| if (next instanceof MergeViewerAction) { |
| MergeViewerAction action = (MergeViewerAction) next; |
| if (action.isContentDependent()) |
| action.update(); |
| } |
| } |
| } |
| |
| /* |
| * Allows the viewer to add menus and/or tools to the context menu. |
| */ |
| @Override |
| public void menuAboutToShow(IMenuManager menu) { |
| |
| menu.add(new Separator("undo")); //$NON-NLS-1$ |
| addMenu(menu, UNDO_ID); |
| addMenu(menu, REDO_ID); |
| menu.add(new GroupMarker("save")); //$NON-NLS-1$ |
| if (fAddSaveAction) |
| addSave(menu); |
| menu.add(new Separator("file")); //$NON-NLS-1$ |
| |
| menu.add(new Separator("ccp")); //$NON-NLS-1$ |
| addMenu(menu, CUT_ID); |
| addMenu(menu, COPY_ID); |
| addMenu(menu, PASTE_ID); |
| addMenu(menu, DELETE_ID); |
| addMenu(menu, SELECT_ALL_ID); |
| |
| menu.add(new Separator("edit")); //$NON-NLS-1$ |
| addMenu(menu, CHANGE_ENCODING_ID); |
| menu.add(new Separator("find")); //$NON-NLS-1$ |
| addMenu(menu, FIND_ID); |
| |
| menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); |
| |
| menu.add(new Separator("text")); //$NON-NLS-1$ |
| for (Iterator<IAction> iterator = textActions.iterator(); iterator.hasNext();) { |
| IAction action = iterator.next(); |
| menu.add(action); |
| } |
| |
| menu.add(new Separator("rest")); //$NON-NLS-1$ |
| |
| // update all actions |
| // to get undo redo right |
| updateActions(); |
| } |
| |
| private void addMenu(IMenuManager menu, String actionId) { |
| IAction action= getAction(actionId); |
| if (action != null) |
| menu.add(action); |
| } |
| |
| private void addSave(IMenuManager menu) { |
| ICommandService commandService = fContainer.getWorkbenchPart().getSite().getService(ICommandService.class); |
| final Command command= commandService.getCommand(IWorkbenchCommandConstants.FILE_SAVE); |
| |
| final IHandler handler = command.getHandler(); |
| if (handler != null) { |
| if (fSaveContributionItem == null) { |
| fSaveContributionItem = new CommandContributionItem( |
| new CommandContributionItemParameter(fContainer |
| .getWorkbenchPart().getSite(), null, command |
| .getId(), CommandContributionItem.STYLE_PUSH)); |
| } |
| // save is an editable dependent action, ie add only when edit |
| // is possible |
| if (handler.isHandled() && getSourceViewer().isEditable()) |
| menu.add(fSaveContributionItem); |
| } |
| } |
| |
| /** |
| * The viewer is no longer part of the UI, it's a wrapper only. The disposal |
| * doesn't take place while releasing the editor pane's children. The method |
| * have to be called it manually. The wrapped viewer is disposed as a |
| * regular viewer, while disposing the UI. |
| */ |
| public void dispose() { |
| getSourceViewer().removeTextListener(this); |
| getSourceViewer().removeSelectionChangedListener(this); |
| EditorsUI.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener); |
| |
| IOperationHistory history = getHistory(); |
| if (history != null) |
| history.removeOperationHistoryListener(this); |
| } |
| |
| /** |
| * update all actions independent of their type |
| * |
| */ |
| public void updateActions() { |
| Iterator<IAction> e= fActions.values().iterator(); |
| while (e.hasNext()) { |
| IAction next = e.next(); |
| if (next instanceof MergeViewerAction) { |
| MergeViewerAction action = (MergeViewerAction) next; |
| action.update(); |
| } else if (next instanceof FindReplaceAction) { |
| FindReplaceAction action = (FindReplaceAction) next; |
| action.update(); |
| } else if (next instanceof ChangeEncodingAction) { |
| ChangeEncodingAction action = (ChangeEncodingAction) next; |
| action.update(); |
| } |
| } |
| } |
| |
| public void configure(SourceViewerConfiguration configuration) { |
| if (isConfigured ) |
| getSourceViewer().unconfigure(); |
| isConfigured = true; |
| getSourceViewer().configure(configuration); |
| } |
| |
| /** |
| * specific implementation to support a vertical ruler |
| * @param x |
| * @param y |
| * @param width |
| * @param height |
| */ |
| public void setBounds (int x, int y, int width, int height) { |
| if(getSourceViewer().getControl() instanceof Composite){ |
| ((Composite)getSourceViewer().getControl()).setBounds(x, y, width, height); |
| } else { |
| getSourceViewer().getTextWidget().setBounds(x, y, width, height); |
| } |
| } |
| |
| /** |
| * handle show/hide line numbers from editor preferences |
| * @param event |
| */ |
| protected void handlePropertyChangeEvent(PropertyChangeEvent event) { |
| |
| String key= event.getProperty(); |
| |
| if(key.equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER)){ |
| boolean b= EditorsUI.getPreferenceStore().getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER); |
| if (b != fShowLineNumber){ |
| toggleLineNumberRuler(); |
| } |
| } else if (key.equals(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR)) { |
| updateLineNumberColumnPresentation(true); |
| } |
| } |
| |
| /** |
| * Hides or shows line number ruler column based of preference setting |
| */ |
| private void updateLineNumberRuler() { |
| if (!fShowLineNumber) { |
| if (fLineNumberColumn != null) { |
| getSourceViewer().removeVerticalRulerColumn(fLineNumberColumn); |
| } |
| } else { |
| if (fLineNumberColumn == null) { |
| fLineNumberColumn = new LineNumberRulerColumn(); |
| updateLineNumberColumnPresentation(false); |
| } |
| getSourceViewer().addVerticalRulerColumn(fLineNumberColumn); |
| } |
| } |
| |
| private void updateLineNumberColumnPresentation(boolean refresh) { |
| if (fLineNumberColumn == null) |
| return; |
| RGB rgb= getColorFromStore(EditorsUI.getPreferenceStore(), AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR); |
| if (rgb == null) |
| rgb= new RGB(0, 0, 0); |
| ISharedTextColors sharedColors= getSharedColors(); |
| fLineNumberColumn.setForeground(sharedColors.getColor(rgb)); |
| if (refresh) { |
| fLineNumberColumn.redraw(); |
| } |
| } |
| |
| private void layoutViewer() { |
| Control parent= getSourceViewer().getControl(); |
| if (parent instanceof Composite && !parent.isDisposed()) |
| ((Composite) parent).layout(true); |
| } |
| |
| private ISharedTextColors getSharedColors() { |
| return EditorsUI.getSharedTextColors(); |
| } |
| |
| private RGB getColorFromStore(IPreferenceStore store, String key) { |
| RGB rgb= null; |
| if (store.contains(key)) { |
| if (store.isDefault(key)) |
| rgb= PreferenceConverter.getDefaultColor(store, key); |
| else |
| rgb= PreferenceConverter.getColor(store, key); |
| } |
| return rgb; |
| } |
| |
| /** |
| * Toggles line number ruler column. |
| */ |
| private void toggleLineNumberRuler() |
| { |
| fShowLineNumber=!fShowLineNumber; |
| |
| updateLineNumberRuler(); |
| } |
| |
| public void addTextAction(IAction textEditorPropertyAction) { |
| textActions.add(textEditorPropertyAction); |
| } |
| |
| public boolean removeTextAction(IAction textEditorPropertyAction) { |
| return textActions.remove(textEditorPropertyAction); |
| } |
| |
| public void addAction(String id, IAction action) { |
| fActions.put(id, action); |
| } |
| |
| private IOperationHistory getHistory() { |
| if (PlatformUI.getWorkbench() == null) { |
| return null; |
| } |
| return PlatformUI.getWorkbench().getOperationSupport() |
| .getOperationHistory(); |
| } |
| |
| @Override |
| public void historyNotification(OperationHistoryEvent event) { |
| // This method updates the enablement of all content operations |
| // when the undo history changes. It could be localized to UNDO and REDO. |
| IUndoContext context = getUndoContext(); |
| if (context != null && event.getOperation().hasContext(context)) { |
| Display.getDefault().asyncExec(() -> updateContentDependantActions()); |
| } |
| } |
| |
| private IUndoContext getUndoContext() { |
| IUndoManager undoManager = getSourceViewer().getUndoManager(); |
| if (undoManager instanceof IUndoManagerExtension) |
| return ((IUndoManagerExtension)undoManager).getUndoContext(); |
| return null; |
| } |
| |
| /** |
| * @return the wrapped viewer |
| */ |
| public SourceViewer getSourceViewer() { |
| return fSourceViewer; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> T getAdapter(Class<T> adapter) { |
| if (adapter == ITextEditor.class) { |
| return (T) new TextEditorAdapter(); |
| } |
| return null; |
| } |
| } |