/******************************************************************************* * Copyright (c) 2002, 2012 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; import java.util.ArrayList; import java.util.Iterator; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; 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.jface.viewers.Viewer; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.progress.PendingUpdateAdapter; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ElementChangedEvent; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICElementDelta; import org.eclipse.cdt.core.model.IElementChangedListener; import org.eclipse.cdt.core.model.ISourceRange; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.ui.PreferenceConstants; import org.eclipse.cdt.internal.core.model.CShiftData; import org.eclipse.cdt.internal.core.model.SourceManipulation; import org.eclipse.cdt.internal.ui.BaseCElementContentProvider; /** * Manages contents of the outliner. */ public class CContentOutlinerProvider extends BaseCElementContentProvider { /** Tree viewer which handles this content provider. */ TreeViewer treeViewer; /** Translation unit's root. */ ITranslationUnit root; /** Something changed listener. */ private ElementChangedListener fListener; /** Property change listener. */ private IPropertyChangeListener fPropertyListener; /** Flag indicating that we are waiting for a delta to populate the view. */ private boolean fInitialDeltaPending; public CContentOutlinerProvider(TreeViewer viewer) { this(viewer, null); } /** * Creates new content provider for dialog. * * @param viewer * Tree viewer. */ public CContentOutlinerProvider(TreeViewer viewer, IWorkbenchPartSite site) { super(true, true); treeViewer = viewer; final IPreferenceStore store = PreferenceConstants.getPreferenceStore(); setIncludesGrouping(store.getBoolean(PreferenceConstants.OUTLINE_GROUP_INCLUDES)); setNamespacesGrouping(store.getBoolean(PreferenceConstants.OUTLINE_GROUP_NAMESPACES)); setMemberGrouping(store.getBoolean(PreferenceConstants.OUTLINE_GROUP_MEMBERS)); setMacroGrouping(store.getBoolean(PreferenceConstants.OUTLINE_GROUP_MACROS)); } /** * Called by the editor to signal that the content has updated. */ public void contentUpdated() { if (treeViewer != null && !treeViewer.getControl().isDisposed()) { treeViewer.getControl().getDisplay().asyncExec(new Runnable() { @Override public void run() { if (!treeViewer.getControl().isDisposed()) { if (fInitialDeltaPending) { fInitialDeltaPending= false; treeViewer.setInput(root); } else { // setting the selection here causes a secondary editor to scroll // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=191358 // final ISelection sel = treeViewer.getSelection(); // treeViewer.setSelection(updateSelection(sel)); treeViewer.refresh(); } } } }); } } /** * Called after CEditor contents is changed. * Existing elements can change their offset and length. * * @param sdata delta information */ public void contentShift(CShiftData sdata) { try { ICElement[] el = root.getChildren(); for (int i=0; i< el.length; i++) { if (!(el[i] instanceof SourceManipulation)) continue; SourceManipulation sm = (SourceManipulation) el[i]; ISourceRange src = sm.getSourceRange(); int endOffset = src.getStartPos() + src.getLength(); // code BELOW this element changed - do nothing ! if (sdata.getOffset() > endOffset) { continue; } if (sdata.getOffset() < src.getStartPos()) { // code ABOVE this element changed - modify offset sm.setIdPos(src.getIdStartPos() + sdata.getSize(), src.getIdLength()); sm.setPos(src.getStartPos() + sdata.getSize(), src.getLength()); sm.setLines(src.getStartLine() + sdata.getLines(), src.getEndLine() + sdata.getLines()); } else { // code INSIDE of this element changed - modify length sm.setPos(src.getStartPos(), src.getLength() + sdata.getSize()); sm.setLines(src.getStartLine(), src.getEndLine() + sdata.getLines()); } } } catch (CModelException e) {} } /** * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { super.dispose(); if (fListener != null) { CoreModel.getDefault().removeElementChangedListener(fListener); fListener = null; } if (fPropertyListener != null) { PreferenceConstants.getPreferenceStore().removePropertyChangeListener(fPropertyListener); fPropertyListener = null; } } /* * @see org.eclipse.cdt.internal.ui.BaseCElementContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { boolean isTU = newInput instanceof ITranslationUnit; if (isTU) { root = (ITranslationUnit) newInput; if (fListener == null) { fListener= new ElementChangedListener(); CoreModel.getDefault().addElementChangedListener(fListener); fPropertyListener= new PropertyListener(); PreferenceConstants.getPreferenceStore().addPropertyChangeListener(fPropertyListener); } } else { if (fListener != null) { CoreModel.getDefault().removeElementChangedListener(fListener); PreferenceConstants.getPreferenceStore().removePropertyChangeListener(fPropertyListener); fListener= null; fPropertyListener= null; } root= null; } } /** * @see org.eclipse.cdt.internal.ui.BaseCElementContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren(Object element) { Object[] children = null; // Use the deferred manager for the first time (when parsing) if (element instanceof ITranslationUnit) { ITranslationUnit unit= (ITranslationUnit)element; if (!unit.isOpen()) { fInitialDeltaPending= true; children= new Object[] { new PendingUpdateAdapter() }; } } if (children == null) { children = super.getChildren(element); } return children; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ @Override public boolean hasChildren(Object element) { return super.hasChildren(element); } /** * Updates current selection. * * @param sel * Selection to update. * @return Updated selection. */ protected ISelection updateSelection(ISelection sel) { final ArrayList<ICElement> newSelection = new ArrayList<ICElement>(); if (sel instanceof IStructuredSelection) { final Iterator<?> iter = ((IStructuredSelection) sel).iterator(); while (iter.hasNext()) { final Object o = iter.next(); if (o instanceof ICElement) { newSelection.add((ICElement)o); } } } return new StructuredSelection(newSelection); } /** * The element change listener of the C outline viewer. * * @see IElementChangedListener */ class ElementChangedListener implements IElementChangedListener { /** * Default constructor. */ public ElementChangedListener() { // nothing to initialize. } /** * @see org.eclipse.cdt.core.model.IElementChangedListener#elementChanged(org.eclipse.cdt.core.model.ElementChangedEvent) */ @Override public void elementChanged(final ElementChangedEvent e) { if (e.getType() == ElementChangedEvent.POST_SHIFT && e.getDelta() instanceof CShiftData) { contentShift((CShiftData)(e.getDelta())); return; } final ICElementDelta delta = findElement(root, e.getDelta()); if (delta != null) { contentUpdated(); } } /** * Determines is structural change. * * @param cuDelta * Delta to check. * @return <b>true</b> if structural change. */ private boolean isPossibleStructuralChange(ICElementDelta cuDelta) { boolean ret; if (cuDelta.getKind() != ICElementDelta.CHANGED) { ret = true; // add or remove } else { final int flags = cuDelta.getFlags(); if ((flags & ICElementDelta.F_CHILDREN) != 0) { ret = true; } else { ret = (flags & (ICElementDelta.F_CONTENT | ICElementDelta.F_FINE_GRAINED)) == ICElementDelta.F_CONTENT; } } return ret; } /** * Searches for element. * * @param unit * Unit to search in. * @param delta * Delta. * @return Found element. */ protected ICElementDelta findElement(ICElement unit, ICElementDelta delta) { if (delta == null || unit == null) { return null; } final ICElement element = delta.getElement(); if (unit.equals(element)) { if (isPossibleStructuralChange(delta)) { return delta; } return null; } if (element.getElementType() > ICElement.C_UNIT) { return null; } final ICElementDelta[] children = delta.getAffectedChildren(); if (children == null || children.length == 0) { return null; } for (ICElementDelta element2 : children) { final ICElementDelta d = findElement(unit, element2); if (d != null) { return d; } } return null; } } /** * * Property change listener. * * @author P.Tomaszewski */ class PropertyListener implements IPropertyChangeListener { /** * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent event) { String prop = event.getProperty(); if (prop.equals(PreferenceConstants.OUTLINE_GROUP_INCLUDES)) { Object newValue = event.getNewValue(); if (newValue instanceof Boolean) { boolean value = ((Boolean) newValue).booleanValue(); if (areIncludesGroup() != value) { setIncludesGrouping(value); contentUpdated(); } } } else if (prop.equals(PreferenceConstants.OUTLINE_GROUP_NAMESPACES)) { Object newValue = event.getNewValue(); if (newValue instanceof Boolean) { boolean value = ((Boolean) newValue).booleanValue(); if (areNamespacesGroup() != value) { setNamespacesGrouping(value); contentUpdated(); } } } else if (prop.equals(PreferenceConstants.OUTLINE_GROUP_MEMBERS)) { Object newValue = event.getNewValue(); if (newValue instanceof Boolean) { boolean value = ((Boolean) newValue).booleanValue(); if (isMemberGroupingEnabled() != value) { setMemberGrouping(value); contentUpdated(); } } }else if (prop.equals(PreferenceConstants.OUTLINE_GROUP_MACROS)) { Object newValue = event.getNewValue(); if (newValue instanceof Boolean) { boolean value = ((Boolean) newValue).booleanValue(); if (isMacroGroupingEnabled() != value) { setMacroGrouping(value); contentUpdated(); } } } } } }