blob: e123fb8e1852929844393c0ddd003a40f09a727e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 Tasktop Technologies and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.commons.workbench.editors;
import java.util.Iterator;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.ActiveShellExpression;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
/**
* Utility class that enables edit actions, content assist and quick fixing for {@link TextViewer} and
* {@link SourceViewer} controls.
*
* @author Steffen Pingel
* @since 3.7
*/
public class CommonTextSupport {
private class UndoRedoListener implements IOperationHistoryListener {
private TextViewer viewer;
public void historyNotification(OperationHistoryEvent event) {
if (viewer != null && selectionChangedListener != null && Display.getCurrent() != null) {
selectionChangedListener.selectionChanged(new SelectionChangedEvent(viewer, viewer.getSelection()));
}
}
public void setViewer(TextViewer viewer) {
this.viewer = viewer;
}
}
private class TextViewerFocusListener implements FocusListener {
private final boolean spellCheck;
private final TextViewer viewer;
public TextViewerFocusListener(TextViewer viewer, boolean spellCheck) {
this.viewer = viewer;
this.spellCheck = spellCheck;
}
public void focusGained(FocusEvent e) {
if (selectionChangedListener != null) {
selectionChangedListener.selectionChanged(new SelectionChangedEvent(viewer, viewer.getSelection()));
}
activateHandlers(viewer, spellCheck);
}
public void focusLost(FocusEvent e) {
deactivateHandlers();
if (selectionChangedListener != null) {
// make sure selection no text is selected when control looses focus
StyledText st = (StyledText) e.widget;
st.setSelectionRange(st.getCaretOffset(), 0);
// update action enablement
selectionChangedListener.selectionChanged(new SelectionChangedEvent(viewer, StructuredSelection.EMPTY));
}
}
}
private static final String KEY_TEXT_VIEWER = "textViewer"; //$NON-NLS-1$
private static boolean canDoGlobalAction(String actionId, TextViewer textViewer) {
if (actionId.equals(ActionFactory.CUT.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.CUT);
} else if (actionId.equals(ActionFactory.COPY.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.COPY);
} else if (actionId.equals(ActionFactory.PASTE.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.PASTE);
} else if (actionId.equals(ActionFactory.DELETE.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.DELETE);
} else if (actionId.equals(ActionFactory.UNDO.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.UNDO);
} else if (actionId.equals(ActionFactory.REDO.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.REDO);
} else if (actionId.equals(ActionFactory.SELECT_ALL.getId())) {
return textViewer.canDoOperation(ITextOperationTarget.SELECT_ALL);
}
return false;
}
public static boolean canPerformAction(String actionId, Control focusControl) {
TextViewer viewer = getTextViewer(focusControl);
if (viewer != null) {
return canDoGlobalAction(actionId, viewer);
}
if (actionId.equals(ActionFactory.UNDO.getId()) || actionId.equals(ActionFactory.REDO.getId())) {
return false;
}
return true;
}
private static boolean canPerformDirectly(String id, Control control) {
if (control instanceof Text) {
Text text = (Text) control;
if (id.equals(ActionFactory.CUT.getId())) {
text.cut();
return true;
}
if (id.equals(ActionFactory.COPY.getId())) {
text.copy();
return true;
}
if (id.equals(ActionFactory.PASTE.getId())) {
text.paste();
return true;
}
if (id.equals(ActionFactory.SELECT_ALL.getId())) {
text.selectAll();
return true;
}
if (id.equals(ActionFactory.DELETE.getId())) {
int count = text.getSelectionCount();
if (count == 0) {
int caretPos = text.getCaretPosition();
text.setSelection(caretPos, caretPos + 1);
}
text.insert(""); //$NON-NLS-1$
return true;
}
}
return false;
}
public static void doAction(String actionId, Control focusControl) {
if (canPerformDirectly(actionId, focusControl)) {
return;
}
TextViewer viewer = getTextViewer(focusControl);
if (viewer != null) {
doGlobalAction(actionId, viewer);
}
}
private static boolean doGlobalAction(String actionId, TextViewer textViewer) {
if (actionId.equals(ActionFactory.CUT.getId())) {
textViewer.doOperation(ITextOperationTarget.CUT);
return true;
} else if (actionId.equals(ActionFactory.COPY.getId())) {
textViewer.doOperation(ITextOperationTarget.COPY);
return true;
} else if (actionId.equals(ActionFactory.PASTE.getId())) {
textViewer.doOperation(ITextOperationTarget.PASTE);
return true;
} else if (actionId.equals(ActionFactory.DELETE.getId())) {
textViewer.doOperation(ITextOperationTarget.DELETE);
return true;
} else if (actionId.equals(ActionFactory.UNDO.getId())) {
textViewer.doOperation(ITextOperationTarget.UNDO);
return true;
} else if (actionId.equals(ActionFactory.REDO.getId())) {
textViewer.doOperation(ITextOperationTarget.REDO);
return true;
} else if (actionId.equals(ActionFactory.SELECT_ALL.getId())) {
textViewer.doOperation(ITextOperationTarget.SELECT_ALL);
return true;
}
return false;
}
public static TextViewer getTextViewer(Widget widget) {
if (widget instanceof StyledText) {
Object data = widget.getData(KEY_TEXT_VIEWER);
if (data instanceof TextViewer) {
return (TextViewer) data;
}
}
return null;
}
public static void setTextViewer(Widget widget, TextViewer textViewer) {
widget.setData(KEY_TEXT_VIEWER, textViewer);
}
public IHandlerActivation contentAssistHandlerActivation;
private final IHandlerService handlerService;
private IHandlerActivation quickAssistHandlerActivation;
private ISelectionChangedListener selectionChangedListener;
private final UndoRedoListener undoRedoListener;
public CommonTextSupport(IHandlerService handlerService) {
Assert.isNotNull(handlerService);
this.handlerService = handlerService;
this.undoRedoListener = new UndoRedoListener();
}
private IHandlerActivation activateHandler(TextViewer viewer, int operation, String actionDefinitionId) {
IHandler handler = createActionHandler(viewer, operation, actionDefinitionId);
return handlerService.activateHandler(actionDefinitionId, handler, //
new ActiveShellExpression(viewer.getTextWidget().getShell()));
}
private void activateHandlers(TextViewer viewer, boolean spellCheck) {
deactivateHandlers();
if (spellCheck) {
quickAssistHandlerActivation = activateHandler(viewer, ISourceViewer.QUICK_ASSIST,
ITextEditorActionDefinitionIds.QUICK_ASSIST);
}
contentAssistHandlerActivation = activateHandler(viewer, ISourceViewer.CONTENTASSIST_PROPOSALS,
ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
undoRedoListener.setViewer(viewer);
OperationHistoryFactory.getOperationHistory().addOperationHistoryListener(undoRedoListener);
}
public void configure(final TextViewer viewer, Document document, boolean spellCheck) {
if (spellCheck && viewer instanceof ISourceViewer) {
configureAsEditor((ISourceViewer) viewer, document);
} else {
viewer.setDocument(document);
}
install(viewer, spellCheck);
}
/** Configures annotation model for spell checking. */
private void configureAsEditor(ISourceViewer viewer, Document document) {
IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
final SourceViewerDecorationSupport support = new SourceViewerDecorationSupport(viewer, null, annotationAccess,
EditorsUI.getSharedTextColors());
Iterator<?> e = new MarkerAnnotationPreferences().getAnnotationPreferences().iterator();
while (e.hasNext()) {
support.setAnnotationPreference((AnnotationPreference) e.next());
}
support.install(EditorsUI.getPreferenceStore());
viewer.getTextWidget().addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
support.uninstall();
}
});
AnnotationModel annotationModel = new AnnotationModel();
viewer.setDocument(document, annotationModel);
}
private IHandler createActionHandler(final ITextOperationTarget viewer, final int operation,
String actionDefinitionId) {
Action action = new Action() {
@Override
public void run() {
if (viewer.canDoOperation(operation)) {
viewer.doOperation(operation);
}
}
};
action.setActionDefinitionId(actionDefinitionId);
return new ActionHandler(action);
}
private void deactivateHandlers() {
if (quickAssistHandlerActivation != null) {
handlerService.deactivateHandler(quickAssistHandlerActivation);
quickAssistHandlerActivation = null;
}
if (contentAssistHandlerActivation != null) {
handlerService.deactivateHandler(contentAssistHandlerActivation);
contentAssistHandlerActivation = null;
}
undoRedoListener.setViewer(null);
OperationHistoryFactory.getOperationHistory().removeOperationHistoryListener(undoRedoListener);
}
public void dispose() {
deactivateHandlers();
}
public ISelectionChangedListener getSelectionChangedListener() {
return selectionChangedListener;
}
public void install(final TextViewer viewer, boolean spellCheck) {
viewer.getControl().addFocusListener(new TextViewerFocusListener(viewer, spellCheck));
if (selectionChangedListener != null) {
viewer.addSelectionChangedListener(selectionChangedListener);
}
setTextViewer(viewer.getControl(), viewer);
}
public void setSelectionChangedListener(ISelectionChangedListener selectionChangedListener) {
this.selectionChangedListener = selectionChangedListener;
}
}