blob: cd6684c33d250d517ecd51ac8c77e61df133a7b7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2006 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal.activities.ws;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.activities.ActivitiesPreferencePage;
import org.eclipse.ui.activities.IActivity;
import org.eclipse.ui.activities.ICategory;
import org.eclipse.ui.activities.ICategoryActivityBinding;
import org.eclipse.ui.activities.IMutableActivityManager;
import org.eclipse.ui.activities.NotDefinedException;
/**
* A simple control provider that will allow the user to toggle on/off the
* activities bound to categories.
*
* @since 3.0
*/
public class ActivityEnabler {
private static final int ALL = 2;
private static final int NONE = 0;
private static final int SOME = 1;
private ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
Object element = ((IStructuredSelection) event.getSelection())
.getFirstElement();
try {
if (element instanceof ICategory) {
descriptionText.setText(((ICategory) element)
.getDescription());
} else if (element instanceof IActivity) {
descriptionText.setText(((IActivity) element)
.getDescription());
}
} catch (NotDefinedException e) {
descriptionText.setText(""); //$NON-NLS-1$
}
}
};
/**
* Listener that manages the grey/check state of categories.
*/
private ICheckStateListener checkListener = new ICheckStateListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent)
*/
public void checkStateChanged(CheckStateChangedEvent event) {
Set checked = new HashSet(Arrays.asList(dualViewer
.getCheckedElements()));
Object element = event.getElement();
if (element instanceof ICategory) {
// clicking on a category should enable/disable all activities
// within it
dualViewer.setSubtreeChecked(element, event.getChecked());
// the state of the category is always absolute after clicking
// on it. Never gray.
dualViewer.setGrayed(element, false);
Object categoryActivities[] = provider.getChildren(element);
// Update the category's activities for multiplicity in other
// categories
for (int index = 0; index < categoryActivities.length; index++) {
handleDuplicateActivities(event.getChecked(),
categoryActivities[index]);
}
} else {
// Activity checked
handleActivityCheck(checked, element);
handleDuplicateActivities(event.getChecked(), element);
}
}
/**
* Handle duplicate activities.
*
* @param checkedState
* Checked state of the element.
* @param element
* The checked element.
*/
private void handleDuplicateActivities(boolean checkedState,
Object element) {
// Retrieve duplicate activities from the other categories
Object[] duplicateActivities = provider
.getDuplicateCategoryActivities((CategorizedActivity) element);
CategorizedActivity activity = null;
for (int index = 0; index < duplicateActivities.length; index++) {
activity = (CategorizedActivity) duplicateActivities[index];
// Update the duplicate activity with the same state as the
// original
dualViewer.setChecked(activity, checkedState);
// handle the activity check to potentially update its
// category's enablement
handleActivityCheck(new HashSet(Arrays.asList(dualViewer
.getCheckedElements())), activity);
}
}
/**
* Handle the checking of an activity and update its category's checked
* state.
*
* @param checked
* The set of checked elements in the viewer.
* @param element
* The checked element.
*/
private void handleActivityCheck(Set checked, Object element) {
// clicking on an activity can potentially change the check/gray
// state of its category.
CategorizedActivity proxy = (CategorizedActivity) element;
Object[] children = provider.getChildren(proxy.getCategory());
int state = NONE;
int count = 0;
for (int i = 0; i < children.length; i++) {
if (checked.contains(children[i])) {
count++;
}
}
if (count == children.length) {
state = ALL;
} else if (count != 0) {
state = SOME;
}
if (state == NONE) {
checked.remove(proxy.getCategory());
} else {
checked.add(proxy.getCategory());
}
dualViewer.setGrayed(proxy.getCategory(), state == SOME);
dualViewer.setCheckedElements(checked.toArray());
// Check child required activities and uncheck parent required activities
// if needed
handleRequiredActivities(checked, element);
}
/**
* Handle the activity's required activities (parent and child).
*
* @param checked
* The set of checked elements in the viewer.
* @param element
* The checked element.
*
*/
private void handleRequiredActivities(Set checked, Object element) {
Object[] requiredActivities = null;
// An element has been checked - we want to check its child required
// activities
if (checked.contains(element)) {
requiredActivities = provider
.getChildRequiredActivities(((CategorizedActivity) element)
.getId());
for (int index = 0; index < requiredActivities.length; index++) {
// We want to check the element if it is unchecked
if (!checked.contains(requiredActivities[index])) {
dualViewer.setChecked(requiredActivities[index], true);
handleActivityCheck(new HashSet(Arrays
.asList(dualViewer.getCheckedElements())),
requiredActivities[index]);
}
}
}
// An element has been unchecked - we want to uncheck its parent
// required activities
else {
requiredActivities = provider
.getParentRequiredActivities(((CategorizedActivity) element)
.getId());
for (int index = 0; index < requiredActivities.length; index++) {
// We want to uncheck the element if it is checked
if (checked.contains(requiredActivities[index])) {
dualViewer.setChecked(requiredActivities[index], false);
handleActivityCheck(new HashSet(Arrays
.asList(dualViewer.getCheckedElements())),
requiredActivities[index]);
}
}
}
}
};
protected CheckboxTreeViewer dualViewer;
/**
* The Set of activities that belong to at least one category.
*/
private Set managedActivities = new HashSet(7);
/**
* The content provider.
*/
protected ActivityCategoryContentProvider provider = new ActivityCategoryContentProvider();
/**
* The descriptive text.
*/
protected Text descriptionText;
private Properties strings;
private IMutableActivityManager activitySupport;
/**
* Create a new instance.
*
* @param activitySupport
* the <code>IMutableActivityMananger</code> to use.
* @param strings
* map of strings to use. See the constants on
* {@link org.eclipse.ui.activities.ActivitiesPreferencePage} for
* details.
*/
public ActivityEnabler(IMutableActivityManager activitySupport, Properties strings) {
this.activitySupport = activitySupport;
this.strings = strings;
}
/**
* Create the controls.
*
* @param parent
* the parent in which to create the controls.
* @return the composite in which the controls exist.
*/
public Control createControl(Composite parent) {
GC gc = new GC(parent);
gc.setFont(JFaceResources.getDialogFont());
FontMetrics fontMetrics = gc.getFontMetrics();
gc.dispose();
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(createGridLayoutWithoutMargins(1, fontMetrics));
new Label(composite, SWT.NONE).setText(strings.getProperty(ActivitiesPreferencePage.ACTIVITY_NAME, ActivityMessages.ActivityEnabler_activities) + ':');
dualViewer = new CheckboxTreeViewer(composite);
dualViewer.setComparator(new ViewerComparator());
dualViewer.setLabelProvider(new ActivityCategoryLabelProvider());
dualViewer.setContentProvider(provider);
dualViewer.setInput(activitySupport);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
dualViewer.getControl().setLayoutData(data);
Composite buttonComposite = new Composite(composite, SWT.NONE);
buttonComposite.setLayout(createGridLayoutWithoutMargins(2, fontMetrics));
Button selectAllButton = new Button(buttonComposite, SWT.PUSH);
selectAllButton.setText(ActivityMessages.ActivityEnabler_selectAll);
selectAllButton.addSelectionListener(new SelectionAdapter() {
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e) {
toggleTreeEnablement(true);
}
});
setButtonLayoutData(selectAllButton, fontMetrics);
Button deselectAllButton = new Button(buttonComposite, SWT.PUSH);
deselectAllButton.setText(ActivityMessages.ActivityEnabler_deselectAll);
deselectAllButton.addSelectionListener(new SelectionAdapter() {
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e) {
toggleTreeEnablement(false);
}
});
setButtonLayoutData(deselectAllButton, fontMetrics);
new Label(composite, SWT.NONE).setText(ActivityMessages.ActivityEnabler_description);
descriptionText = new Text(composite, SWT.READ_ONLY | SWT.WRAP | SWT.BORDER
| SWT.V_SCROLL);
data = new GridData(SWT.FILL, SWT.FILL, true, false);
data.heightHint = Dialog.convertHeightInCharsToPixels(fontMetrics, 5);
descriptionText.setLayoutData(data);
setInitialStates();
dualViewer.addCheckStateListener(checkListener);
dualViewer.addSelectionChangedListener(selectionListener);
dualViewer.setSelection(new StructuredSelection());
Dialog.applyDialogFont(composite);
return composite;
}
private GridLayout createGridLayoutWithoutMargins(int nColumns, FontMetrics fontMetrics) {
GridLayout layout = new GridLayout(nColumns, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.horizontalSpacing = Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.HORIZONTAL_SPACING);
layout.verticalSpacing = Dialog.convertVerticalDLUsToPixels(fontMetrics, IDialogConstants.VERTICAL_SPACING);
return layout;
}
private GridData setButtonLayoutData(Button button, FontMetrics fontMetrics) {
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics, IDialogConstants.BUTTON_WIDTH);
Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
data.widthHint = Math.max(widthHint, minSize.x);
button.setLayoutData(data);
return data;
}
/**
* @param categoryId
* the id to fetch.
* @return return all ids for activities that are in the given in the
* category.
*/
private Collection getCategoryActivityIds(String categoryId) {
ICategory category = activitySupport.getCategory(
categoryId);
Set activityBindings = category.getCategoryActivityBindings();
List categoryActivities = new ArrayList(activityBindings.size());
for (Iterator i = activityBindings.iterator(); i.hasNext();) {
ICategoryActivityBinding binding = (ICategoryActivityBinding) i
.next();
String activityId = binding.getActivityId();
categoryActivities.add(activityId);
}
return categoryActivities;
}
/**
* Set the enabled category/activity check/grey states based on initial
* activity enablement.
*/
private void setInitialStates() {
Set enabledActivities = activitySupport
.getEnabledActivityIds();
setEnabledStates(enabledActivities);
}
private void setEnabledStates(Set enabledActivities) {
Set categories = activitySupport
.getDefinedCategoryIds();
List checked = new ArrayList(10), grayed = new ArrayList(10);
for (Iterator i = categories.iterator(); i.hasNext();) {
String categoryId = (String) i.next();
ICategory category = activitySupport
.getCategory(categoryId);
int state = NONE;
Collection activities = getCategoryActivityIds(categoryId);
int foundCount = 0;
for (Iterator j = activities.iterator(); j.hasNext();) {
String activityId = (String) j.next();
managedActivities.add(activityId);
if (enabledActivities.contains(activityId)) {
IActivity activity = activitySupport
.getActivity(activityId);
checked.add(new CategorizedActivity(category, activity));
//add activity proxy
foundCount++;
}
}
if (foundCount == activities.size()) {
state = ALL;
} else if (foundCount > 0) {
state = SOME;
}
if (state == NONE) {
continue;
}
checked.add(category);
if (state == SOME) {
grayed.add(category);
}
}
dualViewer.setCheckedElements(checked.toArray());
dualViewer.setGrayedElements(grayed.toArray());
}
/**
* Update activity enablement based on the check states of activities in the
* tree.
*/
public void updateActivityStates() {
Set enabledActivities = new HashSet(activitySupport
.getEnabledActivityIds());
// remove all but the unmanaged activities (if any).
enabledActivities.removeAll(managedActivities);
Object[] checked = dualViewer.getCheckedElements();
for (int i = 0; i < checked.length; i++) {
Object element = checked[i];
if (element instanceof ICategory || dualViewer.getGrayed(element)) {
continue;
}
enabledActivities.add(((IActivity) element).getId());
}
activitySupport.setEnabledActivityIds(enabledActivities);
}
/**
* Restore the default activity states.
*/
public void restoreDefaults() {
Set defaultEnabled = new HashSet();
Set activityIds = activitySupport.getDefinedActivityIds();
for (Iterator i = activityIds.iterator(); i.hasNext();) {
String activityId = (String) i.next();
IActivity activity = activitySupport.getActivity(activityId);
try {
if (activity.isDefaultEnabled()) {
defaultEnabled.add(activityId);
}
} catch (NotDefinedException e) {
// this can't happen - we're iterating over defined activities.
}
}
setEnabledStates(defaultEnabled);
}
/**
* Toggles the enablement state of all activities.
*
* @param enabled
* whether the tree should be enabled
*/
protected void toggleTreeEnablement(boolean enabled) {
Object[] elements = provider.getElements(activitySupport);
//reset grey state to null
dualViewer.setGrayedElements(new Object[0]);
//enable all categories
for (int i = 0; i < elements.length; i++) {
dualViewer
.expandToLevel(elements[i], AbstractTreeViewer.ALL_LEVELS);
dualViewer.setSubtreeChecked(elements[i], enabled);
}
}
}