blob: f44b6ef844b46e99e2952c99392fac1665e3af28 [file] [log] [blame]
/*******************************************************************************
* 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
* Stefan Dirix (sdirix@eclipsesource.com) - Bug 473847: Minimum E4 Compatibility of Compare
*******************************************************************************/
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 (IAction action : textActions) {
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.isWorkbenchRunning()) {
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;
}
}