blob: 0d3d2f7dec2f2a556f2cf8bdb5139666c034488c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Anton Leherbauer (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.workingsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.internal.ui.CPluginImages;
import org.eclipse.cdt.internal.ui.actions.SelectionConverter;
import org.eclipse.cdt.internal.ui.viewsupport.AppearanceAwareLabelProvider;
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
import org.eclipse.cdt.internal.ui.viewsupport.DecoratingCLabelProvider;
import org.eclipse.cdt.ui.CElementGrouping;
import org.eclipse.cdt.ui.CElementSorter;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.IWorkingSetPage;
/**
* The C element working set page allows the user to create
* and edit a C element working set.
* <p>
* Working set elements are presented as a C element tree.
* </p>
*
*/
public class CElementWorkingSetPage extends WizardPage implements IWorkingSetPage {
final private static String PAGE_TITLE = WorkingSetMessages.CElementWorkingSetPage_title;
final private static String PAGE_ID = "CElementWorkingSetPage"; //$NON-NLS-1$
private final static int SIZING_SELECTION_WIDGET_WIDTH = 50;
private final static int SIZING_SELECTION_WIDGET_HEIGHT = 200;
private Text fWorkingSetName;
private CheckboxTreeViewer fTree;
private IWorkingSet fWorkingSet;
private boolean fFirstCheck; // set to true if selection is set in setSelection
private ITreeContentProvider fTreeContentProvider;
/**
* Creates a new instance of the receiver.
*/
public CElementWorkingSetPage() {
super(PAGE_ID, PAGE_TITLE, CPluginImages.DESC_WIZABAN_C_APP);
setDescription(WorkingSetMessages.CElementWorkingSetPage_description);
fFirstCheck = true;
}
/*
* @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createControl(Composite parent) {
initializeDialogUnits(parent);
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout());
composite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
setControl(composite);
Label label = new Label(composite, SWT.WRAP);
label.setText(WorkingSetMessages.CElementWorkingSetPage_name);
GridData gd = new GridData(
GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
label.setLayoutData(gd);
fWorkingSetName = new Text(composite, SWT.SINGLE | SWT.BORDER);
fWorkingSetName.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL));
fWorkingSetName.addModifyListener(e -> validateInput());
fWorkingSetName.setFocus();
label = new Label(composite, SWT.WRAP);
label.setText(WorkingSetMessages.CElementWorkingSetPage_content);
gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
label.setLayoutData(gd);
fTree = new CheckboxTreeViewer(composite);
gd = new GridData(GridData.FILL_BOTH | GridData.GRAB_VERTICAL);
gd.heightHint = SIZING_SELECTION_WIDGET_HEIGHT;
gd.widthHint = SIZING_SELECTION_WIDGET_WIDTH;
fTree.getControl().setLayoutData(gd);
fTreeContentProvider = new CElementWorkingSetPageContentProvider();
fTree.setContentProvider(fTreeContentProvider);
AppearanceAwareLabelProvider cElementLabelProvider = new AppearanceAwareLabelProvider(
AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS,
AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS | CElementImageProvider.SMALL_ICONS);
fTree.setLabelProvider(new DecoratingCLabelProvider(cElementLabelProvider));
fTree.setComparator(new CElementSorter());
fTree.setUseHashlookup(true);
fTree.setInput(CoreModel.create(CUIPlugin.getWorkspace().getRoot()));
fTree.addCheckStateListener(event -> handleCheckStateChange(event));
fTree.addTreeListener(new ITreeViewerListener() {
@Override
public void treeCollapsed(TreeExpansionEvent event) {
}
@Override
public void treeExpanded(TreeExpansionEvent event) {
final Object element = event.getElement();
if (fTree.getGrayed(element) == false)
BusyIndicator.showWhile(getShell().getDisplay(),
() -> setSubtreeChecked(element, fTree.getChecked(element), false));
}
});
if (fWorkingSet != null) {
fWorkingSetName.setText(fWorkingSet.getName());
}
initializeCheckedState();
validateInput();
Dialog.applyDialogFont(composite);
// TODO Set help for the page
// CUIHelp.setHelp(fTree, ICHelpContextIds.C_WORKING_SET_PAGE);
}
/*
* @see org.eclipse.ui.dialogs.IWorkingSetPage#finish()
*/
@Override
public void finish() {
String workingSetName = fWorkingSetName.getText();
ArrayList<Object> elements = new ArrayList<>(10);
findCheckedElements(elements, fTree.getInput());
if (fWorkingSet == null) {
IWorkingSetManager workingSetManager = PlatformUI.getWorkbench().getWorkingSetManager();
fWorkingSet = workingSetManager.createWorkingSet(workingSetName,
elements.toArray(new IAdaptable[elements.size()]));
} else {
// Add inaccessible resources
IAdaptable[] oldItems = fWorkingSet.getElements();
HashSet<IProject> closedWithChildren = new HashSet<>(elements.size());
for (IAdaptable oldItem : oldItems) {
IResource oldResource = null;
if (oldItem instanceof IResource) {
oldResource = (IResource) oldItem;
} else {
oldResource = oldItem.getAdapter(IResource.class);
}
if (oldResource != null && oldResource.isAccessible() == false) {
IProject project = oldResource.getProject();
if (closedWithChildren.contains(project) || elements.contains(project)) {
elements.add(oldItem);
elements.remove(project);
closedWithChildren.add(project);
}
}
}
fWorkingSet.setName(workingSetName);
fWorkingSet.setElements(elements.toArray(new IAdaptable[elements.size()]));
}
}
/*
* @see org.eclipse.ui.dialogs.IWorkingSetPage#getSelection()
*/
@Override
public IWorkingSet getSelection() {
return fWorkingSet;
}
/**
* Called when the checked state of a tree item changes.
*
* @param event the checked state change event.
*/
void handleCheckStateChange(final CheckStateChangedEvent event) {
BusyIndicator.showWhile(getShell().getDisplay(), () -> {
IAdaptable element = (IAdaptable) event.getElement();
boolean state = event.getChecked();
fTree.setGrayed(element, false);
if (isExpandable(element)) {
setSubtreeChecked(element, state, true);
}
updateParentState(element, state);
validateInput();
});
}
private boolean isExpandable(Object element) {
return (element instanceof ICProject || element instanceof ICContainer || element instanceof CElementGrouping
|| element instanceof ICModel || element instanceof IContainer);
}
private void updateParentState(Object child, boolean baseChildState) {
if (child == null)
return;
if (child instanceof IAdaptable) {
IResource resource = ((IAdaptable) child).getAdapter(IResource.class);
if (resource != null && !resource.isAccessible())
return;
}
Object parent = fTreeContentProvider.getParent(child);
if (parent == null)
return;
updateObjectState(parent, baseChildState);
}
private void updateObjectState(Object element, boolean baseChildState) {
boolean allSameState = true;
Object[] children = fTreeContentProvider.getChildren(element);
for (int i = children.length - 1; i >= 0; i--) {
if (fTree.getChecked(children[i]) != baseChildState || fTree.getGrayed(children[i])) {
allSameState = false;
break;
}
}
fTree.setGrayed(element, !allSameState);
fTree.setChecked(element, !allSameState || baseChildState);
updateParentState(element, baseChildState);
}
/**
* Sets the checked state of tree items based on the initial
* working set, if any.
*/
private void initializeCheckedState() {
BusyIndicator.showWhile(getShell().getDisplay(), () -> {
Object[] elements;
if (fWorkingSet == null) {
// Use current part's selection for initialization
IWorkbenchPage page = CUIPlugin.getActivePage();
if (page == null)
return;
IWorkbenchPart part = CUIPlugin.getActivePage().getActivePart();
if (part == null)
return;
try {
elements = SelectionConverter.getStructuredSelection(part).toArray();
for (int i1 = 0; i1 < elements.length; i1++) {
if (elements[i1] instanceof IResource) {
ICElement ce = ((IResource) elements[i1]).getAdapter(ICElement.class);
if (ce != null && ce.exists() && ce.getCProject().isOnSourceRoot((IResource) elements[i1]))
elements[i1] = ce;
}
}
} catch (CModelException e) {
return;
}
} else
elements = fWorkingSet.getElements();
for (int i2 = 0; i2 < elements.length; i2++) {
Object element1 = elements[i2];
if (element1 instanceof IResource) {
IProject project = ((IResource) element1).getProject();
if (!project.isAccessible()) {
elements[i2] = project;
} else {
// for backwards compatibility: adapt to ICElement if possible
if (CoreModel.hasCNature(project)) {
ICElement cElement = CoreModel.getDefault().create((IResource) element1);
if (cElement != null) {
elements[i2] = cElement;
}
}
}
} else if (element1 instanceof ICElement) {
ICProject cProject = ((ICElement) element1).getCProject();
if (cProject != null && !cProject.getProject().isAccessible())
elements[i2] = cProject.getProject();
}
}
fTree.setCheckedElements(elements);
HashSet<Object> parents = new HashSet<>();
for (Object element2 : elements) {
if (isExpandable(element2))
setSubtreeChecked(element2, true, true);
if (element2 instanceof IAdaptable) {
IResource resource = ((IAdaptable) element2).getAdapter(IResource.class);
if (resource != null && !resource.isAccessible())
continue;
}
Object parent = fTreeContentProvider.getParent(element2);
if (parent != null)
parents.add(parent);
}
for (Object object : parents)
updateObjectState(object, true);
});
}
/*
* @see org.eclipse.ui.dialogs.IWorkingSetPage#setSelection(org.eclipse.ui.IWorkingSet)
*/
@Override
public void setSelection(IWorkingSet workingSet) {
if (workingSet == null) {
throw new IllegalArgumentException("Working set must not be null"); //$NON-NLS-1$
}
fWorkingSet = workingSet;
if (getContainer() != null && fWorkingSetName != null) {
fFirstCheck = false;
fWorkingSetName.setText(workingSet.getName());
initializeCheckedState();
validateInput();
}
}
/**
* Sets the checked state of the container's members.
*
* @param parent the parent whose children should be checked/unchecked
* @param state true=check all members in the container. false=uncheck all
* members in the container.
* @param checkExpandedState true=recurse into sub-containers and set the
* checked state. false=only set checked state of members of this container
*/
private void setSubtreeChecked(Object parent, boolean state, boolean checkExpandedState) {
if (!(parent instanceof IAdaptable))
return;
IContainer container = ((IAdaptable) parent).getAdapter(IContainer.class);
if ((!fTree.getExpandedState(parent) && checkExpandedState) || (container != null && !container.isAccessible()))
return;
Object[] children = fTreeContentProvider.getChildren(parent);
for (int i = children.length - 1; i >= 0; i--) {
Object element = children[i];
if (state) {
fTree.setChecked(element, true);
fTree.setGrayed(element, false);
} else
fTree.setGrayChecked(element, false);
if (isExpandable(element))
setSubtreeChecked(element, state, true);
}
}
/**
* Validates the working set name and the checked state of the
* resource tree.
*/
void validateInput() {
String errorMessage = null;
String newText = fWorkingSetName.getText();
if (newText.equals(newText.trim()) == false) {
errorMessage = WorkingSetMessages.CElementWorkingSetPage_warning_nameMustNotBeEmpty;
}
if (newText.isEmpty()) {
if (fFirstCheck) {
setPageComplete(false);
fFirstCheck = false;
return;
}
errorMessage = WorkingSetMessages.CElementWorkingSetPage_warning_nameMustNotBeEmpty;
}
fFirstCheck = false;
if (errorMessage == null && (fWorkingSet == null || newText.equals(fWorkingSet.getName()) == false)) {
IWorkingSet[] workingSets = PlatformUI.getWorkbench().getWorkingSetManager().getWorkingSets();
for (IWorkingSet workingSet : workingSets) {
if (newText.equals(workingSet.getName())) {
errorMessage = WorkingSetMessages.CElementWorkingSetPage_warning_workingSetExists;
}
}
}
if (errorMessage == null && fTree.getCheckedElements().length == 0) {
String infoMessage = WorkingSetMessages.CElementWorkingSetPage_warning_resourceMustBeChecked;
setMessage(infoMessage, INFORMATION);
}
setErrorMessage(errorMessage);
setPageComplete(errorMessage == null);
}
/**
* Collects all checked elements of the given parent.
*
* @param checkedElements the output, list of checked elements
* @param parent the parent to collect checked elements in
*/
private void findCheckedElements(List<Object> checkedElements, Object parent) {
Object[] children = fTreeContentProvider.getChildren(parent);
for (Object element : children) {
if (fTree.getGrayed(element))
findCheckedElements(checkedElements, element);
else if (fTree.getChecked(element))
checkedElements.add(element);
}
}
}