/******************************************************************************* * 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.IMutableActivityManager; import org.eclipse.ui.activities.NotDefinedException; import org.eclipse.ui.internal.activities.InternalActivityHelper; /** * 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; } /** * 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 = InternalActivityHelper .getActivityIdsForCategory(activitySupport, category); 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); } } }