/** * <copyright> * </copyright> * * */ package org.dresdenocl.language.ocl.resource.ocl.ui; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.projection.ProjectionViewer; 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.jface.viewers.TreeSelection; import org.eclipse.swt.SWT; 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.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; /** * A manager class for the highlighting of occurrences and brackets. */ public class OclHighlighting implements ISelectionProvider, ISelectionChangedListener { private final static org.dresdenocl.language.ocl.resource.ocl.ui.OclPositionHelper positionHelper = new org.dresdenocl.language.ocl.resource.ocl.ui.OclPositionHelper(); private List<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>(); private ISelection selection = null; private boolean isHighlightBrackets = true; private org.dresdenocl.language.ocl.resource.ocl.ui.OclColorManager colorManager; private Color bracketColor; private Color black; private StyledText textWidget; private IPreferenceStore preferenceStore; private org.dresdenocl.language.ocl.resource.ocl.ui.OclEditor editor; private ProjectionViewer projectionViewer; private org.dresdenocl.language.ocl.resource.ocl.ui.OclOccurrence occurrence; private org.dresdenocl.language.ocl.resource.ocl.ui.OclBracketSet bracketSet; private Display display; /** * A key and mouse listener for the highlighting. It removes the highlighting * before documents change. No highlighting is set after document changes to * increase the performance. Occurrences are not searched if the caret is still in * the same token to increase the performance. */ private final class UpdateHighlightingListener implements KeyListener, VerifyListener, MouseListener, org.dresdenocl.language.ocl.resource.ocl.IOclBackgroundParsingListener { private boolean changed = false; private int caret = -1; public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { if (changed) { changed = false; return; } refreshHighlighting(); } private void refreshHighlighting() { if (textWidget.isDisposed()) { return; } int textCaret = textWidget.getCaretOffset(); if (textCaret < 0 || textCaret > textWidget.getCharCount()) { return; } if (textCaret != caret) { caret = textCaret; removeHighlighting(); setHighlighting(); updateEObjectSelection(); } } public void verifyText(VerifyEvent e) { occurrence.resetTokenRegion(); removeHighlighting(); changed = true; } public void mouseDoubleClick(MouseEvent e) { } public void mouseDown(MouseEvent e) { } // 1-left click, 2-middle click, public void mouseUp(MouseEvent e) { // 3-right click if (e.button != 1) { return; } refreshHighlighting(); } public void parsingCompleted(Resource resource) { display.asyncExec(new Runnable() { public void run() { refreshHighlighting(); } }); } } /** * <p> * Creates the highlighting manager class. * </p> * * @param textResource the text resource to be provided to other classes * @param sourceviewer the source viewer converts offset between master and slave * documents * @param colorManager the color manager provides highlighting colors * @param editor the editor that uses this highlighting object */ public OclHighlighting(org.dresdenocl.language.ocl.resource.ocl.IOclTextResource textResource, ProjectionViewer projectionViewer, org.dresdenocl.language.ocl.resource.ocl.ui.OclColorManager colorManager, org.dresdenocl.language.ocl.resource.ocl.ui.OclEditor editor) { this.display = Display.getCurrent(); projectionViewer.getSelectionProvider(); this.preferenceStore = org.dresdenocl.language.ocl.resource.ocl.ui.OclUIPlugin.getDefault().getPreferenceStore(); this.editor = editor; this.textWidget = projectionViewer.getTextWidget(); this.projectionViewer = projectionViewer; this.occurrence = new org.dresdenocl.language.ocl.resource.ocl.ui.OclOccurrence(textResource, projectionViewer); this.bracketSet = new org.dresdenocl.language.ocl.resource.ocl.ui.OclBracketSet(); this.colorManager = colorManager; this.isHighlightBrackets = preferenceStore.getBoolean(org.dresdenocl.language.ocl.resource.ocl.ui.OclPreferenceConstants.EDITOR_MATCHING_BRACKETS_CHECKBOX); this.bracketColor = colorManager.getColor(PreferenceConverter.getColor(preferenceStore, org.dresdenocl.language.ocl.resource.ocl.ui.OclPreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR)); this.black = colorManager.getColor(new RGB(0, 0, 0)); addListeners(editor); } private void addListeners(org.dresdenocl.language.ocl.resource.ocl.ui.OclEditor editor) { UpdateHighlightingListener hl = new UpdateHighlightingListener(); textWidget.addKeyListener(hl); textWidget.addVerifyListener(hl); textWidget.addMouseListener(hl); editor.addBackgroundParsingListener(hl); } private void setHighlighting() { IDocument document = projectionViewer.getDocument(); if (isHighlightBrackets) { int offset = bracketSet.getCaretOffset((ISourceViewer) editor.getViewer(), textWidget); bracketSet.findAndHighlightMatchingBrackets(document, offset); } occurrence.updateOccurrenceAnnotations(); setBracketHighlighting(document); } private void setBracketHighlighting(IDocument document) { StyleRange styleRange = null; Position[] positions = positionHelper.getPositions(document, org.dresdenocl.language.ocl.resource.ocl.ui.OclPositionCategory.BRACKET.toString()); for (Position position : positions) { Position tmpPosition = convertToWidgetPosition(position); if (tmpPosition != null) { styleRange = getStyleRangeAtPosition(tmpPosition); styleRange.borderStyle = SWT.BORDER_SOLID; styleRange.borderColor = bracketColor; if (styleRange.foreground == null) { styleRange.foreground = black; } textWidget.setStyleRange(styleRange); } } } private void removeHighlighting() { IDocument document = projectionViewer.getDocument(); // remove highlighted matching brackets removeHighlightingCategory(document, org.dresdenocl.language.ocl.resource.ocl.ui.OclPositionCategory.BRACKET.toString()); } private void removeHighlightingCategory(IDocument document, String category) { Position[] positions = positionHelper.getPositions(document, category); if (category.equals(org.dresdenocl.language.ocl.resource.ocl.ui.OclPositionCategory.BRACKET.toString())) { StyleRange styleRange; for (Position position : positions) { Position tmpPosition = convertToWidgetPosition(position); if (tmpPosition != null) { styleRange = getStyleRangeAtPosition(tmpPosition); styleRange.borderStyle = SWT.NONE; styleRange.borderColor = null; styleRange.background = null; textWidget.setStyleRange(styleRange); } } } positionHelper.removePositions(document, category); } /** * Updates the currently selected EObject and notifies registered selection * listeners (e.g., the outline page) about this asynchronously. */ public void updateEObjectSelection() { display.asyncExec(new Runnable() { public void run() { EObject selectedEObject = occurrence.getEObjectAtCurrentPosition(); if (selectedEObject != null) { setSelection(new org.dresdenocl.language.ocl.resource.ocl.ui.OclEObjectSelection(selectedEObject)); } } }); } /** * Resets the changed values after setting the preference pages. */ public void resetValues() { isHighlightBrackets = preferenceStore.getBoolean(org.dresdenocl.language.ocl.resource.ocl.ui.OclPreferenceConstants.EDITOR_MATCHING_BRACKETS_CHECKBOX); bracketColor = colorManager.getColor(PreferenceConverter.getColor(preferenceStore, org.dresdenocl.language.ocl.resource.ocl.ui.OclPreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR)); bracketSet.resetBrackets(preferenceStore); } private Position convertToWidgetPosition(Position position) { if (position == null) { return null; } int startOffset = projectionViewer.modelOffset2WidgetOffset(position.offset); int endOffset = projectionViewer.modelOffset2WidgetOffset(position.offset + position.length); if (endOffset - startOffset != position.length || startOffset == -1 || textWidget.getCharCount() < endOffset) { return null; } return new Position(startOffset, endOffset - startOffset); } private StyleRange getStyleRangeAtPosition(Position position) { StyleRange styleRange = null; try { styleRange = textWidget.getStyleRangeAtOffset(position.offset); } catch (IllegalArgumentException iae) { } if (styleRange == null) { styleRange = new StyleRange(position.offset, position.length, black, null); } else { styleRange.length = position.length; } return styleRange; } public void addSelectionChangedListener(ISelectionChangedListener listener) { selectionChangedListeners.add(listener); } public void removeSelectionChangedListener(ISelectionChangedListener listener) { selectionChangedListeners.remove(listener); } /** * Updates the current selection and notifies registered selection listeners * (e.g., the outline page) about this. */ public void setSelection(ISelection selection) { this.selection = selection; SelectionChangedEvent event = new SelectionChangedEvent(this, selection); for (ISelectionChangedListener listener : selectionChangedListeners) { listener.selectionChanged(event); } } public ISelection getSelection() { return selection; } /** * This method is called by the outline page if its selection was changed. This is * accomplished by adding this class as selection change listener to the outline * page, which is performed by the editor. */ public void selectionChanged(SelectionChangedEvent event) { if (event.getSelection() instanceof TreeSelection) { handleContentOutlineSelection(event.getSelection()); } } /** * Notifies the editor that the selection in the outline page has changed. This * method assumes that the origin of the selection is the outline page or its tree * viewer. */ private void handleContentOutlineSelection(ISelection selection) { if (selection.isEmpty()) { // Ignore empty selections return; } editor.setSelection(selection); } }