/*=============================================================================# # Copyright (c) 2007-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 org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IPageLayout; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.editors.text.IEncodingSupport; import org.eclipse.ui.part.IPageSite; import org.eclipse.ui.part.IShowInSource; import org.eclipse.ui.part.IShowInTarget; import org.eclipse.ui.part.IShowInTargetList; import org.eclipse.ui.part.ShowInContext; import org.eclipse.ui.services.IServiceLocator; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import de.walware.ecommons.ui.SharedUIResources; import de.walware.ecommons.ui.actions.HandlerCollection; import de.walware.ecommons.ui.util.UIAccess; import de.walware.ecommons.ui.workbench.AbstractEditorOutlinePage; import de.walware.ecommons.ltk.AstInfo; import de.walware.ecommons.ltk.IModelElementDelta; import de.walware.ecommons.ltk.core.ISourceModelStamp; import de.walware.ecommons.ltk.core.model.IModelElement; import de.walware.ecommons.ltk.core.model.IModelElement.Filter; import de.walware.ecommons.ltk.core.model.ISourceStructElement; import de.walware.ecommons.ltk.core.model.ISourceUnit; import de.walware.ecommons.ltk.core.model.ISourceUnitModelInfo; import de.walware.ecommons.ltk.internal.ui.EditingMessages; import de.walware.ecommons.ltk.ui.IModelElementInputListener; import de.walware.ecommons.ltk.ui.ISelectionWithElementInfoListener; import de.walware.ecommons.ltk.ui.LTKInputData; /** * Abstract content outline page for a {@link SourceEditor1} with model info. */ public abstract class SourceEditor1OutlinePage extends AbstractEditorOutlinePage implements IContentOutlinePage, IAdaptable, ISourceEditorAssociated, IShowInSource, IShowInTargetList, IShowInTarget, IPostSelectionProvider, IModelElementInputListener { protected class OutlineContent implements OutlineContentProvider.IOutlineContent { public OutlineContent() { } @Override public ISourceUnitModelInfo getModelInfo(final Object input) { return SourceEditor1OutlinePage.this.getModelInfo(input); } @Override public Filter getContentFilter() { return SourceEditor1OutlinePage.this.getContentFilter(); } } public class AstContentProvider extends OutlineContentProvider { public AstContentProvider() { super(new OutlineContent()); } @Override public ISourceModelStamp getStamp(final Object inputElement) { if (inputElement instanceof ISourceUnit) { final AstInfo ast= ((ISourceUnit) inputElement).getAstInfo(fMainType, false, null); if (ast != null) { return ast.getStamp(); } } return null; } @Override public Object[] getElements(final Object inputElement) { if (inputElement instanceof ISourceUnit) { final AstInfo ast= ((ISourceUnit) inputElement).getAstInfo(fMainType, false, null); if (ast != null) { fCurrentModelStamp= ast.getStamp(); return new Object[] { ast.root }; } } return new Object[0]; } } /** * @deprecated use {@link AbstractToggleHandler} */ @Deprecated protected abstract class ToggleAction extends Action { private final String fSettingsKey; private final int fTime; public ToggleAction(final String checkSettingsKey, final boolean checkSettingsDefault, final int expensive) { assert (checkSettingsKey != null); fSettingsKey = checkSettingsKey; fTime = expensive; final IDialogSettings settings = getDialogSettings(); final boolean on = (settings.get(fSettingsKey) == null) ? checkSettingsDefault : getDialogSettings().getBoolean(fSettingsKey); setChecked(on); configure(on); } protected void init() { } @Override public void run() { final Runnable runnable = new Runnable() { @Override public void run() { final boolean on = isChecked(); configure(on); getDialogSettings().put(fSettingsKey, on); } }; if (fTime == 0) { runnable.run(); } else { BusyIndicator.showWhile(Display.getCurrent(), runnable); } } protected abstract void configure(boolean on); } private class SyncWithEditorAction extends ToggleAction implements ISelectionWithElementInfoListener { public SyncWithEditorAction() { super("sync.editor", true, 0); //$NON-NLS-1$ setText(EditingMessages.SyncWithEditor_label); setImageDescriptor(SharedUIResources.getImages().getDescriptor(SharedUIResources.LOCTOOL_SYNCHRONIZED_IMAGE_ID)); } @Override protected void configure(final boolean on) { if (on) { fEditor.addPostSelectionWithElementInfoListener(this); } else { fEditor.removePostSelectionWithElementInfoListener(this); } } @Override public void inputChanged() { } @Override public void stateChanged(final LTKInputData state) { if (!state.isStillValid()) { return; } if (!isUpToDate(state.getInputInfo().getStamp())) { elementUpdatedInfo(state.getInputElement(), null); } UIAccess.getDisplay().syncExec(new Runnable() { @Override public void run() { if (state.isStillValid() && isChecked()) { select(state.getModelSelection()); } } }); } } private final SourceEditor1 fEditor; private final String fMainType; private OutlineContentProvider fContentProvider; private ISourceModelStamp fCurrentModelStamp; private IModelElement fInputUnit; private SyncWithEditorAction fSyncWithEditorAction; public SourceEditor1OutlinePage(final SourceEditor1 editor, final String mainType, final String contextMenuId) { super(contextMenuId); if (editor == null) { throw new NullPointerException(); } if (mainType == null) { throw new NullPointerException(); } fEditor = editor; fMainType = mainType; } @Override public void init(final IPageSite pageSite) { super.init(pageSite); pageSite.setSelectionProvider(this); } protected boolean isUpToDate(final ISourceModelStamp stamp) { final ISourceModelStamp current= fCurrentModelStamp; return (current != null && current.equals(stamp)); } protected IModelElement.Filter getContentFilter() { return null; } @Override public void createControl(final Composite parent) { super.createControl(parent); fEditor.getModelInputProvider().addListener(this); getViewer().setInput(fInputUnit); } protected OutlineContentProvider createContentProvider() { return new OutlineContentProvider(new OutlineContent()); } @Override protected void configureViewer(final TreeViewer viewer) { fContentProvider = createContentProvider(); viewer.setContentProvider(fContentProvider); } @Override protected void initActions(final IServiceLocator serviceLocator, final HandlerCollection handlers) { super.initActions(serviceLocator, handlers); fSyncWithEditorAction = new SyncWithEditorAction(); } @Override protected void contributeToActionBars(final IServiceLocator serviceLocator, final IActionBars actionBars, final HandlerCollection handlers) { super.contributeToActionBars(serviceLocator, actionBars, handlers); actionBars.setGlobalActionHandler(ITextEditorActionConstants.UNDO, fEditor.getAction(ITextEditorActionConstants.UNDO)); actionBars.setGlobalActionHandler(ITextEditorActionConstants.REDO, fEditor.getAction(ITextEditorActionConstants.REDO)); // actionBars.setGlobalActionHandler(ITextEditorActionConstants.NEXT, fEditor.getAction(ITextEditorActionConstants.NEXT)); actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_NEXT_ANNOTATION, fEditor.getAction(ITextEditorActionConstants.NEXT)); // actionBars.setGlobalActionHandler(ITextEditorActionConstants.PREVIOUS, fEditor.getAction(ITextEditorActionConstants.PREVIOUS)); actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_PREVIOUS_ANNOTATION, fEditor.getAction(ITextEditorActionConstants.PREVIOUS)); final IMenuManager menuManager = actionBars.getMenuManager(); menuManager.add(fSyncWithEditorAction); final IContextService service= (IContextService) serviceLocator.getService(IContextService.class); service.activateContext("de.walware.ecommons.ltk.contexts.EditSource1MenuSet"); //$NON-NLS-1$ } @Override protected void contextMenuAboutToShow(final IMenuManager m) { final Separator additions = new Separator(SharedUIResources.ADDITIONS_MENU_ID); m.add(additions); } @Override public void elementChanged(final IModelElement element) { fInputUnit = element; fCurrentModelStamp= null; final TreeViewer viewer = getViewer(); if (UIAccess.isOkToUse(viewer)) { viewer.setInput(fInputUnit); } } @Override public void elementInitialInfo(final IModelElement element) { elementUpdatedInfo(element, null); } @Override public void elementUpdatedInfo(final IModelElement element, final IModelElementDelta delta) { if (element != fInputUnit || (element == null && fInputUnit == null)) { return; } final Display display = UIAccess.getDisplay(); display.syncExec(new Runnable() { @Override public void run() { final TreeViewer viewer = getViewer(); if (element != fInputUnit || !UIAccess.isOkToUse(viewer) || isUpToDate(fContentProvider.getStamp(element)) ) { return; } beginIgnoreSelection(); try { viewer.refresh(true); } finally { endIgnoreSelection(false); } } }); } protected ISourceUnitModelInfo getModelInfo(final Object input) { if (input instanceof ISourceUnit) { return ((ISourceUnit) input).getModelInfo(fMainType, 0, null); } return null; } @Override public void dispose() { fEditor.getModelInputProvider().removeListener(this); fEditor.handleOutlinePageClosed(); super.dispose(); } @Override protected void selectInEditor(final ISelection selection) { fEditor.setSelection(selection, fSyncWithEditorAction); } protected void select(ISourceStructElement element) { final TreeViewer viewer = getViewer(); if (UIAccess.isOkToUse(viewer)) { beginIgnoreSelection(); try { final Filter filter = getContentFilter(); Object selectedElement = null; final IStructuredSelection currentSelection = ((IStructuredSelection) viewer.getSelection()); if (currentSelection.size() == 1) { selectedElement = currentSelection.getFirstElement(); } while (element != null && (element.getElementType() & IModelElement.MASK_C2) != IModelElement.C2_SOURCE_FILE) { if (selectedElement != null && element.equals(selectedElement)) { return; } if (filter == null || filter.include(element)) { selectedElement = null; viewer.setSelection(new StructuredSelection(element), true); if (!viewer.getSelection().isEmpty()) { return; } } final IModelElement parent = element.getSourceParent(); if (parent instanceof ISourceStructElement) { element = (ISourceStructElement) parent; continue; } else { break; } } if (!viewer.getSelection().isEmpty()) { viewer.setSelection(StructuredSelection.EMPTY); } } finally { endIgnoreSelection(true); } } } @Override public ShowInContext getShowInContext() { return new ShowInContext(fEditor.getEditorInput(), null); } @Override public String[] getShowInTargetIds() { return new String[] { IPageLayout.ID_PROJECT_EXPLORER }; } @Override public boolean show(final ShowInContext context) { final IModelElement inputUnit = fInputUnit; final ISelection selection = context.getSelection(); if (selection instanceof LTKInputData) { final LTKInputData data = (LTKInputData) selection; data.update(); if (inputUnit.equals(data.getInputElement())) { select(data.getModelSelection()); return true; } } return false; } @Override public ISourceEditor getSourceEditor() { return fEditor; } @Override public Object getAdapter(final Class required) { if (required == ISourceEditorAssociated.class) { return this; } if (required == IEncodingSupport.class) { return fEditor.getAdapter(IEncodingSupport.class); } if (required == IContentType.class) { return fEditor.getContentType(); } return null; } }