/******************************************************************************* * Copyright (c) 2010 itemis AG (http://www.itemis.eu) * 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: * Jan Koehnlein - Initial API and implementation *******************************************************************************/ package com.github.jknack.antlr4ide.ui.railroad.actions; import org.eclipse.draw2d.IFigure; import org.eclipse.emf.common.util.URI; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Region; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.widgets.Display; import org.eclipse.xtext.Constants; import org.eclipse.xtext.ui.editor.IURIEditorOpener; import org.eclipse.xtext.ui.editor.XtextEditor; import com.github.jknack.antlr4ide.ui.railroad.RailroadSelectionProvider; import com.github.jknack.antlr4ide.ui.railroad.RailroadView; import com.github.jknack.antlr4ide.ui.railroad.RailroadViewPreferences; import com.github.jknack.antlr4ide.ui.railroad.figures.IEObjectReferer; import com.github.jknack.antlr4ide.ui.railroad.figures.ISelectable; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; @Singleton public class RailroadSelectionLinker implements IPropertyChangeListener { @Inject @Named(Constants.LANGUAGE_NAME) private String language; @Inject private IURIEditorOpener uriEditorOpener; @Inject private RailroadView view; private XtextEditor currentEditor; private ISelectable currentSelectedNode; @Inject private RailroadViewPreferences preferences; private ISelectionChangedListener diagramSelectionChangeListener = new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { if (view.getControl().isFocusControl()) { diagramSelectionChanged(event); } } }; private ISelectionChangedListener textSelectionChangeListener = new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { if (preferences.isLinkWithEditor() && view.getSite().getPage().getActivePart() != view) { textSelectionChanged(event); } } }; public void activate() { view.getSite().getSelectionProvider().addSelectionChangedListener(diagramSelectionChangeListener); preferences.getPreferenceStore().addPropertyChangeListener(this); if (preferences.isLinkWithEditor()) { selectGrammarText(currentSelectedNode, false); } } public void deactivate() { view.getSite().getSelectionProvider().removeSelectionChangedListener(diagramSelectionChangeListener); if (currentEditor != null) { removeTextSelectionListener(currentEditor); } preferences.getPreferenceStore().removePropertyChangeListener(this); } protected void selectFigure(IFigure selectedFigure) { while (selectedFigure != null && !(selectedFigure instanceof IEObjectReferer)) { selectedFigure = selectedFigure.getParent(); } if (selectedFigure != null) { if (currentSelectedNode != null) { currentSelectedNode.setSelected(false); } if (selectedFigure instanceof ISelectable) { ((ISelectable) selectedFigure).setSelected(true); currentSelectedNode = (ISelectable) selectedFigure; } else { currentSelectedNode = null; } } } protected void selectGrammarText(final IFigure selectedFigure, final boolean isActivateEditor) { if (selectedFigure != null) { IEObjectReferer element = (IEObjectReferer) selectedFigure; final URI grammarElementURI = element.getEObjectURI(); if (grammarElementURI != null) { final boolean isSelectElement = selectedFigure instanceof ISelectable; // enqueue to make sure the diagram is updated before if (isActivateEditor && isSelectElement) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { uriEditorOpener.open(grammarElementURI, isSelectElement); } }); } else { uriEditorOpener.open(grammarElementURI, isSelectElement); view.getSite().getPage().activate(view); } } } } public void diagramSelectionChanged(final SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection) selection; if (structuredSelection.size() == 1) { Object selectedElement = structuredSelection.getFirstElement(); if (selectedElement instanceof IFigure) { IFigure selectedFigure = (IFigure) selectedElement; selectFigure(selectedFigure); boolean isDoubleClick = event instanceof RailroadSelectionProvider.DoubleClickEvent; if ((currentEditor == null || !currentEditor.getInternalSourceViewer().getTextWidget() .isFocusControl()) && (isDoubleClick || preferences.isLinkWithEditor())) { selectGrammarText(selectedFigure, isDoubleClick); } } } } } public void textSelectionChanged(final SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof ITextSelection && !selection.isEmpty()) { ITextSelection textSelection = (ITextSelection) selection; int offset = textSelection.getOffset(); IFigure figureToBeSelected = findFigureForTextOffset(view.getContents(), offset, null); if (figureToBeSelected != null) { selectFigure(figureToBeSelected); view.reveal(figureToBeSelected); } } } protected ISelectable findFigureForTextOffset(final IFigure figure, final int offset, ISelectable currentBestFigure) { if(figure == null) { return null; } if (figure instanceof ISelectable) { Region textRegion = ((ISelectable) figure).getTextRegion(); if (textRegion != null && textRegion.getOffset() <= offset && textRegion.getOffset() + textRegion.getLength() >= offset) { if(currentBestFigure == null || currentBestFigure.getTextRegion().getLength() > textRegion.getLength()) { currentBestFigure = (ISelectable) figure; } } } for (Object child : figure.getChildren()) { if (child instanceof IFigure) { currentBestFigure = findFigureForTextOffset((IFigure) child, offset, currentBestFigure); } } return currentBestFigure; } @Override public void propertyChange(final PropertyChangeEvent event) { if (event.getProperty().equals(RailroadViewPreferences.LINK_WITH_EDITOR_KEY) && event.getNewValue() == Boolean.TRUE) { if (currentSelectedNode != null) { selectGrammarText(currentSelectedNode, true); } } } public void setXtextEditor(final XtextEditor xtextEditor) { if (currentEditor != null) { removeTextSelectionListener(currentEditor); } if (language.equals(xtextEditor.getLanguageName())) { currentEditor = xtextEditor; ISelectionProvider selectionProvider = xtextEditor.getSelectionProvider(); if (selectionProvider instanceof IPostSelectionProvider) { ((IPostSelectionProvider) selectionProvider) .addPostSelectionChangedListener(textSelectionChangeListener); } else { selectionProvider.addSelectionChangedListener(textSelectionChangeListener); } } } protected void removeTextSelectionListener(final XtextEditor editor) { ISelectionProvider selectionProvider = editor.getSelectionProvider(); if (selectionProvider != null) { if (selectionProvider instanceof IPostSelectionProvider) { ((IPostSelectionProvider) selectionProvider) .removePostSelectionChangedListener(textSelectionChangeListener); } else { selectionProvider.removeSelectionChangedListener(textSelectionChangeListener); } } } }