| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |