/****************************************************************************** * Copyright (c) 2007, 2008 Edgar Espina. * 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 * *******************************************************************************/ package org.deved.antlride.ui.text; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.deved.antlride.core.model.IGrammar; import org.deved.antlride.core.model.IModelElement; import org.deved.antlride.core.model.ast.ModelElementQuery; import org.deved.antlride.internal.ui.views.interpreter.AntlrInterpreterMessages; import org.deved.antlride.ui.AntlrPreferenceConstants; import org.deved.antlride.ui.AntlrUIHelper; import org.eclipse.jface.action.Action; 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.resource.JFaceResources; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.ITextOperationTarget; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.source.CompositeRuler; import org.eclipse.jface.text.source.IVerticalRuler; import org.eclipse.jface.text.source.IVerticalRulerColumn; import org.eclipse.jface.text.source.LineNumberRulerColumn; import org.eclipse.jface.text.source.SourceViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ExtendedModifyEvent; import org.eclipse.swt.custom.ExtendedModifyListener; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.IUpdate; public class AntlrInputSourceViewer extends Viewer { private IGrammar fGrammar; private InputSourceViewerListener fListener; private Map<String, IAction> fGlobalActions; private MenuManager fMenuManager; private SourceViewer fSourceViewer; private Composite fComposite; private Label fStatusBar; private class InputSourceViewerListener implements ExtendedModifyListener, ISelectionChangedListener, KeyListener, MouseListener { public void modifyText(ExtendedModifyEvent event) { StyledText styledText = (StyledText) event.widget; String text = styledText.getText(); if (text != null && text.length() > 0) { highlightSyntax(text); } } public void selectionChanged(SelectionChangedEvent event) { updateAction(ITextEditorActionConstants.CUT); updateAction(ITextEditorActionConstants.COPY); } public void keyPressed(KeyEvent e) { updateStatusLine(); } public void keyReleased(KeyEvent e) { } public void mouseDoubleClick(MouseEvent e) { } public void mouseDown(MouseEvent e) { updateStatusLine(); } public void mouseUp(MouseEvent e) { } } private class TextViewerAction extends Action implements IUpdate { private int fOperationCode = -1; private ITextOperationTarget fOperationTarget; /** * Creates a new action. * * @param viewer * the viewer * @param operationCode * the opcode */ public TextViewerAction(ITextViewer viewer, int operationCode) { fOperationCode = operationCode; fOperationTarget = viewer.getTextOperationTarget(); update(); } /** * Updates the enabled state of the action. Fires a property change if * the enabled state changes. * * @see Action#firePropertyChange(String, Object, Object) */ public void update() { boolean wasEnabled = isEnabled(); boolean isEnabled = (fOperationTarget != null && fOperationTarget .canDoOperation(fOperationCode)); setEnabled(isEnabled); if (wasEnabled != isEnabled) { firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE); } } /** * @see Action#run() */ public void run() { if (fOperationCode != -1 && fOperationTarget != null) { fOperationTarget.doOperation(fOperationCode); } } } private class ShowMenuListener implements IMenuListener { public void menuAboutToShow(IMenuManager manager) { onMenuAboutToShow(manager); } } public AntlrInputSourceViewer(Composite parent, boolean editable) { createControl(parent, editable); } public AntlrInputSourceViewer(Composite parent) { this(parent, true); } public void setEditable(boolean editable) { fSourceViewer.setEditable(editable); } protected void createControl(Composite parent, boolean editable) { fComposite = new Composite(parent, SWT.BORDER); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; fComposite.setLayout(layout); IVerticalRuler verticalRuler = createVerticalRuler(); int style = SWT.MULTI | SWT.V_SCROLL | SWT.BORDER; fSourceViewer = new SourceViewer(fComposite, verticalRuler, style); fSourceViewer.setDocument(new Document()); fSourceViewer.setEditable(editable); // configure text fListener = new InputSourceViewerListener(); StyledText styledText = fSourceViewer.getTextWidget(); styledText.setFont(getDefaultFont()); styledText.addExtendedModifyListener(fListener); styledText.addMouseListener(fListener); styledText.addKeyListener(fListener); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.horizontalSpan = 2; fSourceViewer.getControl().setLayoutData(gd); createTextActions(); fSourceViewer.addSelectionChangedListener(fListener); fMenuManager = new MenuManager(); fMenuManager.addMenuListener(new ShowMenuListener()); fMenuManager.setRemoveAllWhenShown(true); Menu menu = fMenuManager.createContextMenu(styledText); styledText.setMenu(menu); // status bar gd = new GridData(SWT.RIGHT, SWT.FILL, true, false); gd.widthHint = 40; fStatusBar = new Label(fComposite, SWT.LEFT); fStatusBar.setLayoutData(gd); updateStatusLine(); Label separator = new Label(fComposite, SWT.SEPARATOR); // separator.setText(" "); gd = new GridData(); gd.widthHint = 8; gd.heightHint = 8; separator.setLayoutData(gd); } public StyledText getTextWidget() { return fSourceViewer.getTextWidget(); } private void updateStatusLine() { StyledText styledText = getTextWidget(); StringBuilder buf = new StringBuilder(); int line = styledText.getLineAtOffset(styledText.getCaretOffset()); buf.append(line + 1); buf.append(" : "); buf.append(styledText.getCaretOffset() - styledText.getOffsetAtLine(line) + 1); // buf.append(" "); fStatusBar.setText(buf.toString()); } private static Font getDefaultFont() { return JFaceResources.getFont("Monospaced"); } protected void onMenuAboutToShow(IMenuManager menuManager) { String[] actions = { ITextEditorActionConstants.CUT, ITextEditorActionConstants.COPY, ITextEditorActionConstants.PASTE, null, ITextEditorActionConstants.SELECT_ALL }; for (String actionId : actions) { IAction action = fGlobalActions.get(actionId); if (action == null) { menuManager.add(new Separator()); } else { menuManager.add(action); } } menuManager.update(); } private void updateAction(String actionId) { IAction action = (IAction) fGlobalActions.get(actionId); if (action instanceof IUpdate) ((IUpdate) action).update(); } private void createTextActions() { fGlobalActions = new HashMap<String, IAction>(); ISharedImages sharedImages = PlatformUI.getWorkbench() .getSharedImages(); TextViewerAction action; action = new TextViewerAction(fSourceViewer, ITextOperationTarget.CUT); action.setAccelerator(SWT.CTRL | 'X'); action.setText(AntlrInterpreterMessages.GrammarInterpreter_Cut); action.setImageDescriptor(sharedImages .getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); action.setDisabledImageDescriptor(sharedImages .getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED)); fGlobalActions.put(ITextEditorActionConstants.CUT, action); action = new TextViewerAction(fSourceViewer, ITextOperationTarget.COPY); action.setAccelerator(SWT.CTRL | 'C'); action.setText(AntlrInterpreterMessages.GrammarInterpreter_Copy); action.setImageDescriptor(sharedImages .getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); action.setDisabledImageDescriptor(sharedImages .getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); fGlobalActions.put(ITextEditorActionConstants.COPY, action); action = new TextViewerAction(fSourceViewer, ITextOperationTarget.PASTE); action.setAccelerator(SWT.CTRL | 'V'); action.setText(AntlrInterpreterMessages.GrammarInterpreter_Paste); action.setImageDescriptor(sharedImages .getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); action.setDisabledImageDescriptor(sharedImages .getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED)); fGlobalActions.put(ITextEditorActionConstants.PASTE, action); action = new TextViewerAction(fSourceViewer, ITextOperationTarget.SELECT_ALL); action.setText(AntlrInterpreterMessages.GrammarInterpreter_Select_All); fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action); } public void dispose() { fGrammar = null; fListener = null; fComposite.dispose(); } private void highlightSyntax(String text) { IGrammar grammar = fGrammar; if (grammar == null) return; StyledText styledText = getTextWidget(); IModelElement[] literals = ModelElementQuery.collectLiterals(grammar); if (literals != null && literals.length > 0) { Pattern pattern = Pattern.compile("(\\w(\\w|\\d)*)|(\\S)"); Matcher matcher = pattern.matcher(text); List<StyleRange> styleRanges = new ArrayList<StyleRange>(); while (matcher.find()) { String word = matcher.group(); if (word.length() > 1) { for (IModelElement literal : literals) { String elementName = literal.getElementName() .substring(1, literal.getElementName().length() - 1); if (elementName.equals(word)) { int start = matcher.start(); int length = matcher.end() - matcher.start(); styleRanges.add(createStyleRange(start, length)); break; } } } } styledText.setStyleRanges(styleRanges .toArray(new StyleRange[styleRanges.size()])); } } private StyleRange createStyleRange(int start, int length) { StyleRange styleRange = new StyleRange(); styleRange.start = start; styleRange.length = length; styleRange.fontStyle = SWT.BOLD; styleRange.foreground = JFaceResources.getColorRegistry().get( AntlrPreferenceConstants.EDITOR_KEYWORD_COLOR); return styleRange; } public IGrammar getGrammar() { return fGrammar; } public void setGrammar(IGrammar grammar) { fGrammar = grammar; } private static IVerticalRuler createVerticalRuler() { CompositeRuler ruler = new CompositeRuler(); ruler.addDecorator(0, createLineNumberRulerColumn()); return ruler; } private static IVerticalRulerColumn createLineNumberRulerColumn() { LineNumberRulerColumn verticalRulerColumn = new LineNumberRulerColumn(); Color color = Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY); verticalRulerColumn.setFont(getDefaultFont()); verticalRulerColumn.setForeground(color); return verticalRulerColumn; } public IAction getAction(String actionId) { return fGlobalActions.get(actionId); } @Override public Control getControl() { return fComposite; } @Override public Object getInput() { return fSourceViewer.getInput(); } @Override public ISelection getSelection() { return fSourceViewer.getSelection(); } @Override public void refresh() { fSourceViewer.refresh(); } @Override public void setInput(Object input) { if (input instanceof String) { input = new Document((String) input); } fSourceViewer.setInput(input); highlightSyntax(((Document) fSourceViewer.getInput()).get()); } @Override public void setSelection(ISelection selection, boolean reveal) { AntlrUIHelper.select(getTextWidget(), (AntlrTextSelection) selection); } @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { fSourceViewer.addSelectionChangedListener(listener); } }