/*****************************************************************************
* Copyright (c) 2010 Atos Origin.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Jeremie Belmudes (Atos Origin) Jeremie.Belmudes@atosorigin.com - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.views.documentation.view;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.Action;
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.action.SubMenuManager;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
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.swt.SWT;
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.graphics.Color;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.ActiveShellExpression;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
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;
/**
* A composite used to edit some documentation in a plain text mode working with the eclipse spell checking tool.<br>
* Creation : 2 july 2010<br>
*
* @author jbelmudes
*/
public class SpellingTextComposite extends Composite implements IText, Observer
{
protected StyledText fTextField; // updated only by modify events
protected SourceViewer sourceViewer;
protected Document document;
protected final static String CURRENT_LINE = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE;
protected final static String CURRENT_LINE_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR;
private IHandlerActivation quickFixhandlerActivation;
/**
* @param composite the parent of this Composite
*/
public SpellingTextComposite(final Composite composite)
{
this(composite, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL, false);
}
/**
* @param composite the parent of this Composite
* @param style the style of the text control
*/
public SpellingTextComposite(final Composite composite, int style)
{
this(composite, style, false);
}
/**
* @param composite the parent of this Composite
* @param style the style of the text control
* @param colored tells if the widget has a colored line where the cursor is set
*/
public SpellingTextComposite(final Composite composite, int style, boolean colored)
{
super(composite, SWT.BORDER);
this.setLayout(new FillLayout());
AnnotationModel annotationModel = new AnnotationModel();
IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
sourceViewer = new SourceViewer(this, null, null, true, style);
fTextField = sourceViewer.getTextWidget();
final SourceViewerDecorationSupport support = new SourceViewerDecorationSupport(sourceViewer, null, annotationAccess, EditorsUI.getSharedTextColors());
// display or not a colored line where the field is edited
if (colored)
{
support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
}
Iterator<?> e = new MarkerAnnotationPreferences().getAnnotationPreferences().iterator();
while (e.hasNext())
support.setAnnotationPreference((AnnotationPreference) e.next());
support.install(EditorsUI.getPreferenceStore());
final IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
final ActionHandler quickFixActionHandler = createQuickFixActionHandler(sourceViewer);
final TextViewerAction cutAction = new TextViewerAction(sourceViewer, ITextOperationTarget.CUT);
cutAction.setText(Messages.SpellingTextComposite_cut);
cutAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_CUT);
final TextViewerAction copyAction = new TextViewerAction(sourceViewer, ITextOperationTarget.COPY);
copyAction.setText(Messages.SpellingTextComposite_copy);
copyAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
final TextViewerAction pasteAction = new TextViewerAction(sourceViewer, ITextOperationTarget.PASTE);
pasteAction.setText(Messages.SpellingTextComposite_paste);
pasteAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE);
final TextViewerAction selectAllAction = new TextViewerAction(sourceViewer, ITextOperationTarget.SELECT_ALL);
selectAllAction.setText(Messages.SpellingTextComposite_selectAll);
selectAllAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
final MenuManager contextMenu = new MenuManager();
contextMenu.add(cutAction);
contextMenu.add(copyAction);
contextMenu.add(pasteAction);
contextMenu.add(selectAllAction);
contextMenu.add(new Separator());
final SubMenuManager quickFixMenu = new SubMenuManager(contextMenu);
quickFixMenu.setVisible(true);
quickFixMenu.addMenuListener(new IMenuListener()
{
public void menuAboutToShow(IMenuManager manager)
{
quickFixMenu.removeAll();
if (fTextField != null && fTextField.getEditable())
{
IAnnotationModel annotationModel = sourceViewer.getAnnotationModel();
Iterator<?> annotationIterator = annotationModel.getAnnotationIterator();
while (annotationIterator.hasNext())
{
Annotation annotation = (Annotation) annotationIterator.next();
if (!annotation.isMarkedDeleted() && includes(annotationModel.getPosition(annotation), sourceViewer.getTextWidget().getCaretOffset())
&& sourceViewer.getQuickAssistAssistant().canFix(annotation))
{
ICompletionProposal[] computeQuickAssistProposals = sourceViewer.getQuickAssistAssistant().getQuickAssistProcessor().computeQuickAssistProposals(
sourceViewer.getQuickAssistInvocationContext());
for (int i = 0; i < computeQuickAssistProposals.length; i++)
{
final ICompletionProposal proposal = computeQuickAssistProposals[i];
quickFixMenu.add(new Action(proposal.getDisplayString())
{
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#run()
*/
public void run()
{
proposal.apply(sourceViewer.getDocument());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#getImageDescriptor()
*/
public ImageDescriptor getImageDescriptor()
{
if (proposal.getImage() != null)
{
return ImageDescriptor.createFromImage(proposal.getImage());
}
return null;
}
});
}
}
}
}
}
});
fTextField.addFocusListener(new FocusListener()
{
private IHandlerActivation cutHandlerActivation;
private IHandlerActivation copyHandlerActivation;
private IHandlerActivation pasteHandlerActivation;
private IHandlerActivation selectAllHandlerActivation;
public void focusGained(FocusEvent e)
{
cutAction.update();
copyAction.update();
IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
this.cutHandlerActivation = service.activateHandler(IWorkbenchCommandConstants.EDIT_CUT, new ActionHandler(cutAction), new ActiveShellExpression(getShell()));
this.copyHandlerActivation = service.activateHandler(IWorkbenchCommandConstants.EDIT_COPY, new ActionHandler(copyAction), new ActiveShellExpression(getShell()));
this.pasteHandlerActivation = service.activateHandler(IWorkbenchCommandConstants.EDIT_PASTE, new ActionHandler(pasteAction), new ActiveShellExpression(getShell()));
this.selectAllHandlerActivation = service.activateHandler(IWorkbenchCommandConstants.EDIT_SELECT_ALL, new ActionHandler(selectAllAction), new ActiveShellExpression(getShell()));
quickFixhandlerActivation = installQuickFixActionHandler(handlerService, sourceViewer, quickFixActionHandler);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
*/
public void focusLost(FocusEvent e)
{
IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
if (quickFixhandlerActivation != null)
{
service.deactivateHandler(quickFixhandlerActivation);
}
if (cutHandlerActivation != null) {
service.deactivateHandler(cutHandlerActivation);
}
if (copyHandlerActivation != null) {
service.deactivateHandler(copyHandlerActivation);
}
if (pasteHandlerActivation != null) {
service.deactivateHandler(pasteHandlerActivation);
}
if (selectAllHandlerActivation != null) {
service.deactivateHandler(selectAllHandlerActivation);
}
}
});
sourceViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
cutAction.update();
copyAction.update();
}
});
sourceViewer.getTextWidget().addDisposeListener(new DisposeListener()
{
public void widgetDisposed(DisposeEvent e)
{
support.uninstall();
if (quickFixhandlerActivation != null)
{
handlerService.deactivateHandler(quickFixhandlerActivation);
}
}
});
document = new Document();
// NOTE: Configuration must be applied before the document is set in order for
// Hyperlink coloring to work. (Presenter needs document object up front)
sourceViewer.configure(new TextSourceViewerConfiguration(EditorsUI.getPreferenceStore()));
sourceViewer.setDocument(document, annotationModel);
fTextField.setMenu(contextMenu.createContextMenu(fTextField));
}
protected boolean includes(Position position, int caretOffset)
{
return position.includes(caretOffset) || (position.offset + position.length) == caretOffset;
}
/**
* Set the color of the texte area background
*/
public void setBackground(Color object)
{
fTextField.setBackground(object);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.widgets.Control#setForeground(org.eclipse.swt.graphics.Color)
*/
public void setForeground(Color color)
{
fTextField.setForeground(color);
}
/**
* Installs the quick fix action handler and returns the handler activation.
*
* @param handlerService the handler service
* @param sourceViewer the source viewer
* @param createQuickFixActionHandler
* @return the handler activation
* @since 3.4
*/
private IHandlerActivation installQuickFixActionHandler(IHandlerService handlerService, SourceViewer sourceViewer, IHandler createQuickFixActionHandler)
{
return handlerService.activateHandler(ITextEditorActionDefinitionIds.QUICK_ASSIST, createQuickFixActionHandler, AlwaysTrue.INSTANCE
/*
* ,/*, new ActiveShellExpression(sourceViewer.getTextWidget().getShell())
*/);
}
/**
* Creates and returns a quick fix action handler.
*
* @param textOperationTarget the target for text operations
* @since 3.4
*/
private ActionHandler createQuickFixActionHandler(final ITextOperationTarget textOperationTarget)
{
Action quickFixAction = new Action()
{
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.action.Action#run()
*/
public void run()
{
textOperationTarget.doOperation(ISourceViewer.QUICK_ASSIST);
}
};
quickFixAction.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
return new ActionHandler(quickFixAction);
}
public void setEnabled(boolean enabled)
{
fTextField.setEditable(enabled);
if (!enabled)
{
fTextField.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
}
else
{
fTextField.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
}
}
public void update(Observable o, Object arg)
{
if (arg instanceof String)
{
setText((String) arg); // triggers a modify event
}
}
/*
* (non-Javadoc)
*
* @see org.topcased.tabbedproperties.sections.widgets.IText#getText()
*/
public String getText()
{
return document.get();
}
/*
* (non-Javadoc)
*
* @see org.topcased.tabbedproperties.sections.widgets.IText#getControl()
*/
public Control getControl()
{
return this;
}
/*
* (non-Javadoc)
*
* @see org.topcased.tabbedproperties.sections.widgets.IText#getTextControl()
*/
public Control getTextControl()
{
return fTextField;
}
/*
* (non-Javadoc)
*
* @see org.topcased.tabbedproperties.sections.widgets.IText#setEditable(boolean)
*/
public void setEditable(boolean isChangeable)
{
fTextField.setEditable(isChangeable);
}
/*
* (non-Javadoc)
*
* @see org.topcased.tabbedproperties.sections.widgets.IText#setText(java.lang.String)
*/
public void setText(String string)
{
document.set(string);
}
private static class AlwaysTrue extends Expression
{
public static final AlwaysTrue INSTANCE = new AlwaysTrue();
private AlwaysTrue ()
{
}
@Override
public EvaluationResult evaluate(IEvaluationContext context) throws CoreException
{
return EvaluationResult.TRUE;
}
}
}