/*=============================================================================#
# Copyright (c) 2008-2016 Stephan Wahlbrink (WalWare.de) and others.
# 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.ecommons.ltk.ui.sourceediting;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
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.commands.ActionHandler;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
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.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.IUpdate;
import de.walware.ecommons.preferences.ui.SettingsUpdater;
import de.walware.ecommons.text.ui.TextViewerAction;
import de.walware.ecommons.text.ui.TextViewerEditorColorUpdater;
import de.walware.ecommons.text.ui.TextViewerJFaceUpdater;
import de.walware.ecommons.ui.actions.ControlServicesUtil;
import de.walware.ecommons.ui.components.WidgetToolsButton;
import de.walware.ecommons.ui.util.LayoutUtil;
import de.walware.ecommons.ui.util.UIAccess;
/**
* Text snippet editor (no Eclipse editor) supporting {@link SourceEditorViewerConfigurator}.
*/
public class SnippetEditor extends Object {
public static final int DEFAULT_SINGLE_LINE_STYLE = SWT.BORDER | SWT.SINGLE | SWT.LEFT_TO_RIGHT;
public static final int DEFAULT_MULTI_LINE_STYLE = SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.LEFT_TO_RIGHT;
private static class ExtStyledText extends StyledText {
private Color fSavedColor;
public ExtStyledText(final Composite parent, final int style) {
super(parent, style);
}
@Override
public void setBackground(final Color color) {
fSavedColor = color;
if (isEnabled()) {
super.setBackground(color);
}
}
@Override
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
if (enabled) {
super.setBackground(fSavedColor);
}
else {
super.setBackground(getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
}
}
}
private class Updater implements ISelectionChangedListener, IDocumentListener, Runnable {
private boolean fActionUpdateScheduled = false;
@Override
public void selectionChanged(final SelectionChangedEvent event) {
schedule();
}
@Override
public void documentAboutToBeChanged(final DocumentEvent event) {
}
@Override
public void documentChanged(final DocumentEvent event) {
schedule();
}
public void schedule() {
if (fActionUpdateScheduled) {
return;
}
Display.getCurrent().asyncExec(this);
fActionUpdateScheduled = true;
}
@Override
public void run() {
fActionUpdateScheduled = false;
if (UIAccess.isOkToUse(fSourceViewer)) {
for (final Action action : fGlobalActions.values()) {
if (action instanceof IUpdate) {
((IUpdate) action).update();
}
}
}
}
}
private final boolean fWithToolButton;
private Composite fComposite;
private final Document fDocument;
private SourceViewer fSourceViewer;
private final SourceEditorViewerConfigurator fConfigurator;
private Map<String, Action> fGlobalActions;
private Updater fUpdater;
private final IServiceLocator fServiceLocator;
private ControlServicesUtil fServiceUtil;
/**
* Creates snippet editor with empty document.
*/
public SnippetEditor(final SourceEditorViewerConfigurator configurator) {
this(configurator, null, null);
}
/**
* Creates snippet editor with initial content.
*/
public SnippetEditor(final SourceEditorViewerConfigurator configurator, final String initialContent,
final IServiceLocator serviceParent) {
this(configurator, initialContent, serviceParent, false);
}
/**
* Creates snippet editor with initial content.
*/
public SnippetEditor(final SourceEditorViewerConfigurator configurator, final String initialContent,
final IServiceLocator serviceParent, final boolean withToolButton) {
fConfigurator = configurator;
fDocument = (initialContent != null) ? new Document(initialContent) : new Document();
fConfigurator.getDocumentSetupParticipant().setup(fDocument);
fServiceLocator = serviceParent;
fWithToolButton = withToolButton;
}
public void create(final Composite parent, final int style) {
if (fWithToolButton) {
fComposite = new Composite(parent, SWT.NONE) {
@Override
public boolean setFocus() {
return fSourceViewer.getTextWidget().setFocus();
}
};
fComposite.setLayout(LayoutUtil.applySashDefaults(new GridLayout(), 2));
createSourceViewer(fComposite, style);
fSourceViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final WidgetToolsButton button = new WidgetToolsButton(fSourceViewer.getTextWidget()) {
@Override
protected void fillMenu(final Menu menu) {
SnippetEditor.this.fillToolMenu(menu);
}
};
button.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
}
else {
createSourceViewer(parent, style);
fComposite = fSourceViewer.getTextWidget();
}
}
private void createSourceViewer(final Composite composite, final int style) {
fSourceViewer = new SourceViewer(composite, null, style) {
@Override
protected StyledText createTextWidget(final Composite parent, final int styles) {
final StyledText styledText = new ExtStyledText(parent, styles);
styledText.setLeftMargin(Math.max(styledText.getLeftMargin(), 2));
return styledText;
}
};
fSourceViewer.setEditable(true);
fSourceViewer.setDocument(fDocument);
final ViewerSourceEditorAdapter adapter = new ViewerSourceEditorAdapter(fSourceViewer, fConfigurator);
fConfigurator.setTarget(adapter);
new TextViewerJFaceUpdater(fSourceViewer,
fConfigurator.getSourceViewerConfiguration().getPreferences() );
new TextViewerEditorColorUpdater(fSourceViewer,
fConfigurator.getSourceViewerConfiguration().getPreferences() );
new SettingsUpdater(fConfigurator, fSourceViewer.getControl());
initActions();
fSourceViewer.activatePlugins();
fUpdater = new Updater();
fSourceViewer.addSelectionChangedListener(fUpdater);
fSourceViewer.getDocument().addDocumentListener(fUpdater);
}
public void addAction(final Action action) {
fGlobalActions.put(action.getId(), action);
final String commandId = action.getActionDefinitionId();
if (fServiceUtil != null && commandId != null) {
fServiceUtil.activateHandler(commandId, new ActionHandler(action));
}
}
public Action getAction(final String id) {
return fGlobalActions.get(id);
}
private void initActions() {
fGlobalActions = new HashMap<>(10);
if (fServiceLocator != null) {
fServiceUtil = new ControlServicesUtil(fServiceLocator, getClass().getName()+'#'+hashCode(), getSourceViewer().getControl());
fServiceUtil.addControl(getSourceViewer().getControl());
}
// default actions
addAction(TextViewerAction.createUndoAction(fSourceViewer));
addAction(TextViewerAction.createRedoAction(fSourceViewer));
addAction(TextViewerAction.createCutAction(fSourceViewer));
addAction(TextViewerAction.createCopyAction(fSourceViewer));
addAction(TextViewerAction.createPasteAction(fSourceViewer));
addAction(TextViewerAction.createSelectAllAction(fSourceViewer));
// create context menu
final MenuManager manager = new MenuManager(null, null);
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager mgr) {
fillContextMenu(mgr);
}
});
final StyledText text = fSourceViewer.getTextWidget();
final Menu menu = manager.createContextMenu(text);
text.setMenu(menu);
}
protected void fillContextMenu(final IMenuManager menu) {
menu.add(new GroupMarker(ITextEditorActionConstants.GROUP_UNDO));
menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, getAction(ITextEditorActionConstants.UNDO));
menu.appendToGroup(ITextEditorActionConstants.GROUP_UNDO, getAction(ITextEditorActionConstants.REDO));
menu.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, getAction(ITextEditorActionConstants.CUT));
menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, getAction(ITextEditorActionConstants.COPY));
menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, getAction(ITextEditorActionConstants.PASTE));
menu.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, getAction(ITextEditorActionConstants.SELECT_ALL));
menu.add(new Separator("assist")); //$NON-NLS-1$
final Action action = fGlobalActions.get("ContentAssistProposal"); //$NON-NLS-1$
if (action != null && action.getText() != null) {
menu.appendToGroup("assist", action); //$NON-NLS-1$
}
}
protected void fillToolMenu(final Menu menu) {
}
public Document getDocument() {
return fDocument;
}
public SourceViewer getSourceViewer() {
return fSourceViewer;
}
public Composite getControl() {
return fComposite;
}
public StyledText getTextControl() {
return fSourceViewer.getTextWidget();
}
public void reset() {
fSourceViewer.resetPlugins();
fUpdater.run();
}
}