/******************************************************************************* * Copyright (c) 2000, 2010 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 * Andrew Gvozdev - some improvements such as adding source folders bug 339015 *******************************************************************************/ package org.eclipse.cdt.make.ui; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePreferenceConstants; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICDescriptionDelta; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; import org.eclipse.cdt.core.settings.model.ICSettingObject; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.make.core.IMakeTarget; import org.eclipse.cdt.make.core.IMakeTargetListener; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.core.MakeTargetEvent; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; /** * Content provider for Make Targets view and for Make Targets dialog from * "Make Targets"->"Build..." in project context menu. * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ public class MakeContentProvider implements ITreeContentProvider, IMakeTargetListener, IResourceChangeListener, ICProjectDescriptionListener, IPreferenceChangeListener { /** presentation of the content, i.e. for MakeView tree of for BuildTargetDialog table */ protected boolean bFlatten; protected StructuredViewer viewer; /** * Default constructor. */ public MakeContentProvider() { this(false); } /** * Constructor. * * @param flat - {@code true} for "flat" representation for a table * or {@code false} to represent as a tree. */ public MakeContentProvider(boolean flat) { bFlatten = flat; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ public Object[] getChildren(Object obj) { if (obj instanceof IWorkspaceRoot) { try { return MakeCorePlugin.getDefault().getTargetManager().getTargetBuilderProjects(); } catch (CoreException e) { MakeCorePlugin.log(e); } } else if (obj instanceof IContainer) { IContainer container = (IContainer)obj; ArrayList<Object> children = new ArrayList<Object>(); boolean isAddingSourceRoots = !bFlatten && (container instanceof IProject) && CCorePlugin.showSourceRootsAtTopOfProject(); // add source roots if necessary if (isAddingSourceRoots) { IProject project = (IProject) container; ICSourceEntry[] srcEntries = getSourceEntries(project); for (ICSourceEntry srcEntry : srcEntries) { if (!srcEntry.getFullPath().equals(project.getFullPath())) { children.add(new TargetSourceContainer(srcEntry)); } } } // add regular folders try { IResource[] resources = container.members(); for (IResource rc : resources) { if (rc instanceof IContainer) { if (!(isAddingSourceRoots && isSourceEntry(rc))) { children.add(rc); } } } } catch (CoreException e) { MakeCorePlugin.log(e); } // finally add targets try { IMakeTarget[] targets = MakeCorePlugin.getDefault().getTargetManager().getTargets(container); children.addAll(Arrays.asList(targets)); } catch (CoreException e) { MakeCorePlugin.log(e); } return children.toArray(); } else if (obj instanceof TargetSourceContainer) { ArrayList<Object> children = new ArrayList<Object>(); try { IContainer container = ((TargetSourceContainer) obj).getContainer(); IResource[] resources = container.members(); for (IResource rc : resources) { if (rc instanceof IContainer) { children.add(rc); } } children.addAll(Arrays.asList(MakeCorePlugin.getDefault().getTargetManager().getTargets(container))); } catch (CoreException e) { MakeCorePlugin.log(e); } return children.toArray(); } return new Object[0]; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) */ public Object getParent(Object obj) { if (obj instanceof IMakeTarget) { // this is ambiguous as make target can sit in 2 places, in its container // or source folder represented by TargetSourceContainer return ((IMakeTarget)obj).getContainer(); } else if (obj instanceof IContainer) { return ((IContainer)obj).getParent(); } else if (obj instanceof TargetSourceContainer) { IContainer container = ((TargetSourceContainer)obj).getContainer(); // TargetSourceContainer sits at project root return container.getProject(); } return null; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ public boolean hasChildren(Object obj) { return getChildren(obj).length > 0; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(java.lang.Object) */ public Object[] getElements(Object obj) { if (bFlatten) { List<Object> list = new ArrayList<Object>(); Object[] children = getChildren(obj); for (int i = 0; i < children.length; i++) { list.add(children[i]); list.addAll(Arrays.asList(getElements(children[i]))); } return list.toArray(); } return getChildren(obj); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ public void dispose() { if (viewer != null) { MakeCorePlugin.getDefault().getTargetManager().removeListener(this); } } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (this.viewer == null) { MakeCorePlugin.getDefault().getTargetManager().addListener(this); } this.viewer = (StructuredViewer) viewer; IWorkspace oldWorkspace = null; IWorkspace newWorkspace = null; if (oldInput instanceof IWorkspace) { oldWorkspace = (IWorkspace) oldInput; } else if (oldInput instanceof IContainer) { oldWorkspace = ((IContainer) oldInput).getWorkspace(); } else if (oldInput instanceof TargetSourceContainer) { oldWorkspace = ((TargetSourceContainer) oldInput).getContainer().getWorkspace(); } if (newInput instanceof IWorkspace) { newWorkspace = (IWorkspace) newInput; } else if (newInput instanceof IContainer) { newWorkspace = ((IContainer) newInput).getWorkspace(); } else if (newInput instanceof TargetSourceContainer) { newWorkspace = ((TargetSourceContainer) newInput).getContainer().getWorkspace(); } if (oldWorkspace != newWorkspace) { ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager(); if (oldWorkspace != null) { InstanceScope.INSTANCE.getNode(CCorePlugin.PLUGIN_ID).removePreferenceChangeListener(this); mngr.removeCProjectDescriptionListener(this); oldWorkspace.removeResourceChangeListener(this); } if (newWorkspace != null) { newWorkspace.addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); mngr.addCProjectDescriptionListener(this, CProjectDescriptionEvent.APPLIED); InstanceScope.INSTANCE.getNode(CCorePlugin.PLUGIN_ID).addPreferenceChangeListener(this); } } } /* (non-Javadoc) * @see org.eclipse.cdt.make.core.IMakeTargetListener#targetChanged(org.eclipse.cdt.make.core.MakeTargetEvent) */ public void targetChanged(final MakeTargetEvent event) { final Control ctrl = viewer.getControl(); if (ctrl != null && !ctrl.isDisposed()) { switch (event.getType()) { case MakeTargetEvent.PROJECT_ADDED : case MakeTargetEvent.PROJECT_REMOVED : ctrl.getDisplay().asyncExec(new Runnable() { public void run() { if (!ctrl.isDisposed()) { viewer.refresh(); } } }); break; case MakeTargetEvent.TARGET_ADD : case MakeTargetEvent.TARGET_CHANGED : case MakeTargetEvent.TARGET_REMOVED : ctrl.getDisplay().asyncExec(new Runnable() { public void run() { if (!ctrl.isDisposed()) { if (bFlatten) { viewer.refresh(); } else { //We can't just call refresh on the container target that //has been created since it may be that the container has //been filtered out and the filters in the viewer don't know //any better how to call out to the filter selection again. //Instead we walk to the root project container and refresh it. Set<IContainer> containers = new HashSet<IContainer>(); IMakeTarget[] targets = event.getTargets(); for (IMakeTarget target : targets) { IContainer container = target.getContainer(); while(!(container instanceof IProject) && container.getParent()!=null) { container = container.getParent(); } containers.add(container); } for (IContainer container : containers) { viewer.refresh(container); } } } } }); break; } } } private void processDelta(IResourceDelta delta) { // Bail out if the widget was disposed. Control ctrl = viewer.getControl(); if (ctrl == null || ctrl.isDisposed() || delta == null) { return; } IResourceDelta[] affectedChildren = delta.getAffectedChildren(IResourceDelta.CHANGED); // Not interested in Content changes. for (int i = 0; i < affectedChildren.length; i++) { if ((affectedChildren[i].getFlags() & IResourceDelta.TYPE) != 0) { return; } } // Handle changed children recursively. for (int i = 0; i < affectedChildren.length; i++) { processDelta(affectedChildren[i]); } // Get the affected resource final IResource resource = delta.getResource(); // Handle removed children. Issue one update for all removals. affectedChildren = delta.getAffectedChildren(IResourceDelta.REMOVED); if (affectedChildren.length > 0) { final ArrayList<IResource> affected = new ArrayList<IResource>(affectedChildren.length); for (int i = 0; i < affectedChildren.length; i++) { if (affectedChildren[i].getResource().getType() == IResource.FOLDER) { affected.add(affectedChildren[i].getResource()); } } if (!affected.isEmpty()) { Display.getDefault().asyncExec(new Runnable() { public void run() { if (viewer == null || viewer.getControl() == null || viewer.getControl().isDisposed()) return; if (viewer instanceof AbstractTreeViewer) { ((AbstractTreeViewer) viewer).remove(affected.toArray()); } else { viewer.refresh(resource); } } }); } } // Handle added children. Issue one update for all insertions. affectedChildren = delta.getAffectedChildren(IResourceDelta.ADDED); if (affectedChildren.length > 0) { final ArrayList<IResource> affected = new ArrayList<IResource>(affectedChildren.length); for (int i = 0; i < affectedChildren.length; i++) { if (affectedChildren[i].getResource().getType() == IResource.FOLDER) { affected.add(affectedChildren[i].getResource()); } } if (!affected.isEmpty()) { Display.getDefault().asyncExec(new Runnable() { public void run() { if (viewer == null || viewer.getControl() == null || viewer.getControl().isDisposed()) return; if (viewer instanceof AbstractTreeViewer) { ((AbstractTreeViewer) viewer).add(resource, affected.toArray()); } else { viewer.refresh(resource); } } }); } } } /* (non-Javadoc) * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) */ public void resourceChanged(IResourceChangeEvent event) { final IResourceDelta delta = event.getDelta(); Control ctrl = viewer.getControl(); if (ctrl != null && !ctrl.isDisposed()) processDelta(delta); } /** * {@inheritDoc} * * @since 7.1 */ public void handleEvent(final CProjectDescriptionEvent event) { Display display = Display.getDefault(); display.asyncExec(new Runnable() { public void run() { ICDescriptionDelta delta = event.getDefaultSettingCfgDelta(); if (delta==null) return; int flags = delta.getChangeFlags(); if ( ((flags & ICDescriptionDelta.SOURCE_ADDED) != 0) || ((flags & ICDescriptionDelta.SOURCE_REMOVED) != 0) ) { IProject project = null; ICSettingObject setting = delta.getOldSetting(); if (setting==null) setting = delta.getNewSetting(); if (setting instanceof ICConfigurationDescription) project = ((ICConfigurationDescription) setting).getProjectDescription().getProject(); if (project!=null) viewer.refresh(project); else viewer.refresh(); } } }); } /** * {@inheritDoc} * * @since 7.1 */ public void preferenceChange(PreferenceChangeEvent event) { if (event.getKey().equals(CCorePreferenceConstants.SHOW_SOURCE_ROOTS_AT_TOP_LEVEL_OF_PROJECT)) { Display.getDefault().asyncExec(new Runnable() { public void run() { viewer.refresh(); } }); } } /** * Get source entries for default setting configuration (i.e. configuration shown in UI). */ private static ICSourceEntry[] getSourceEntries(IProject project) { ICProjectDescriptionManager mgr = CCorePlugin.getDefault().getProjectDescriptionManager(); ICProjectDescription prjDescription = mgr.getProjectDescription(project, false); if (prjDescription!=null) { ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); if (cfgDescription!=null) { ICSourceEntry[] srcEntries = cfgDescription.getResolvedSourceEntries(); return srcEntries; } } return new ICSourceEntry[0]; } /** * Check if the resource is in the list of source entries. * @param rc - resource to check. * @return {@code true} if the resource is a source folder, {@code false} otherwise. * * @since 7.1 */ public static boolean isSourceEntry(IResource rc) { IProject project = rc.getProject(); ICSourceEntry[] srcEntries = getSourceEntries(project); for (ICSourceEntry srcEntry : srcEntries) { if (srcEntry.getFullPath().equals(rc.getFullPath())) return true; } return false; } }