001: /*******************************************************************************
002: * Copyright (c) 2003, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.activities.ws;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Collection;
015: import java.util.HashSet;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Properties;
019: import java.util.Set;
020:
021: import org.eclipse.jface.dialogs.Dialog;
022: import org.eclipse.jface.dialogs.IDialogConstants;
023: import org.eclipse.jface.resource.JFaceResources;
024: import org.eclipse.jface.viewers.AbstractTreeViewer;
025: import org.eclipse.jface.viewers.CheckStateChangedEvent;
026: import org.eclipse.jface.viewers.CheckboxTreeViewer;
027: import org.eclipse.jface.viewers.ICheckStateListener;
028: import org.eclipse.jface.viewers.ISelectionChangedListener;
029: import org.eclipse.jface.viewers.IStructuredSelection;
030: import org.eclipse.jface.viewers.SelectionChangedEvent;
031: import org.eclipse.jface.viewers.StructuredSelection;
032: import org.eclipse.jface.viewers.ViewerComparator;
033: import org.eclipse.swt.SWT;
034: import org.eclipse.swt.events.SelectionAdapter;
035: import org.eclipse.swt.events.SelectionEvent;
036: import org.eclipse.swt.graphics.FontMetrics;
037: import org.eclipse.swt.graphics.GC;
038: import org.eclipse.swt.graphics.Point;
039: import org.eclipse.swt.layout.GridData;
040: import org.eclipse.swt.layout.GridLayout;
041: import org.eclipse.swt.widgets.Button;
042: import org.eclipse.swt.widgets.Composite;
043: import org.eclipse.swt.widgets.Control;
044: import org.eclipse.swt.widgets.Label;
045: import org.eclipse.swt.widgets.Text;
046: import org.eclipse.ui.activities.ActivitiesPreferencePage;
047: import org.eclipse.ui.activities.IActivity;
048: import org.eclipse.ui.activities.ICategory;
049: import org.eclipse.ui.activities.ICategoryActivityBinding;
050: import org.eclipse.ui.activities.IMutableActivityManager;
051: import org.eclipse.ui.activities.NotDefinedException;
052:
053: /**
054: * A simple control provider that will allow the user to toggle on/off the
055: * activities bound to categories.
056: *
057: * @since 3.0
058: */
059: public class ActivityEnabler {
060:
061: private static final int ALL = 2;
062:
063: private static final int NONE = 0;
064:
065: private static final int SOME = 1;
066:
067: private ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
068:
069: /*
070: * (non-Javadoc)
071: *
072: * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
073: */
074: public void selectionChanged(SelectionChangedEvent event) {
075: Object element = ((IStructuredSelection) event
076: .getSelection()).getFirstElement();
077: try {
078: if (element instanceof ICategory) {
079: descriptionText.setText(((ICategory) element)
080: .getDescription());
081: } else if (element instanceof IActivity) {
082: descriptionText.setText(((IActivity) element)
083: .getDescription());
084: }
085: } catch (NotDefinedException e) {
086: descriptionText.setText(""); //$NON-NLS-1$
087: }
088: }
089: };
090:
091: /**
092: * Listener that manages the grey/check state of categories.
093: */
094: private ICheckStateListener checkListener = new ICheckStateListener() {
095:
096: /*
097: * (non-Javadoc)
098: *
099: * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent)
100: */
101: public void checkStateChanged(CheckStateChangedEvent event) {
102: Set checked = new HashSet(Arrays.asList(dualViewer
103: .getCheckedElements()));
104: Object element = event.getElement();
105: if (element instanceof ICategory) {
106: // clicking on a category should enable/disable all activities
107: // within it
108: dualViewer.setSubtreeChecked(element, event
109: .getChecked());
110: // the state of the category is always absolute after clicking
111: // on it. Never gray.
112: dualViewer.setGrayed(element, false);
113: Object categoryActivities[] = provider
114: .getChildren(element);
115: // Update the category's activities for multiplicity in other
116: // categories
117: for (int index = 0; index < categoryActivities.length; index++) {
118: handleDuplicateActivities(event.getChecked(),
119: categoryActivities[index]);
120: }
121:
122: } else {
123: // Activity checked
124: handleActivityCheck(checked, element);
125: handleDuplicateActivities(event.getChecked(), element);
126: }
127: }
128:
129: /**
130: * Handle duplicate activities.
131: *
132: * @param checkedState
133: * Checked state of the element.
134: * @param element
135: * The checked element.
136: */
137: private void handleDuplicateActivities(boolean checkedState,
138: Object element) {
139: // Retrieve duplicate activities from the other categories
140: Object[] duplicateActivities = provider
141: .getDuplicateCategoryActivities((CategorizedActivity) element);
142: CategorizedActivity activity = null;
143: for (int index = 0; index < duplicateActivities.length; index++) {
144: activity = (CategorizedActivity) duplicateActivities[index];
145: // Update the duplicate activity with the same state as the
146: // original
147: dualViewer.setChecked(activity, checkedState);
148: // handle the activity check to potentially update its
149: // category's enablement
150: handleActivityCheck(new HashSet(Arrays
151: .asList(dualViewer.getCheckedElements())),
152: activity);
153: }
154: }
155:
156: /**
157: * Handle the checking of an activity and update its category's checked
158: * state.
159: *
160: * @param checked
161: * The set of checked elements in the viewer.
162: * @param element
163: * The checked element.
164: */
165: private void handleActivityCheck(Set checked, Object element) {
166: // clicking on an activity can potentially change the check/gray
167: // state of its category.
168: CategorizedActivity proxy = (CategorizedActivity) element;
169: Object[] children = provider.getChildren(proxy
170: .getCategory());
171: int state = NONE;
172: int count = 0;
173: for (int i = 0; i < children.length; i++) {
174: if (checked.contains(children[i])) {
175: count++;
176: }
177: }
178:
179: if (count == children.length) {
180: state = ALL;
181: } else if (count != 0) {
182: state = SOME;
183: }
184:
185: if (state == NONE) {
186: checked.remove(proxy.getCategory());
187: } else {
188: checked.add(proxy.getCategory());
189: }
190:
191: dualViewer.setGrayed(proxy.getCategory(), state == SOME);
192: dualViewer.setCheckedElements(checked.toArray());
193: // Check child required activities and uncheck parent required activities
194: // if needed
195: handleRequiredActivities(checked, element);
196: }
197:
198: /**
199: * Handle the activity's required activities (parent and child).
200: *
201: * @param checked
202: * The set of checked elements in the viewer.
203: * @param element
204: * The checked element.
205: *
206: */
207: private void handleRequiredActivities(Set checked,
208: Object element) {
209: Object[] requiredActivities = null;
210: // An element has been checked - we want to check its child required
211: // activities
212: if (checked.contains(element)) {
213: requiredActivities = provider
214: .getChildRequiredActivities(((CategorizedActivity) element)
215: .getId());
216: for (int index = 0; index < requiredActivities.length; index++) {
217: // We want to check the element if it is unchecked
218: if (!checked.contains(requiredActivities[index])) {
219: dualViewer.setChecked(
220: requiredActivities[index], true);
221: handleActivityCheck(new HashSet(
222: Arrays.asList(dualViewer
223: .getCheckedElements())),
224: requiredActivities[index]);
225: }
226: }
227: }
228: // An element has been unchecked - we want to uncheck its parent
229: // required activities
230: else {
231: requiredActivities = provider
232: .getParentRequiredActivities(((CategorizedActivity) element)
233: .getId());
234: for (int index = 0; index < requiredActivities.length; index++) {
235: // We want to uncheck the element if it is checked
236: if (checked.contains(requiredActivities[index])) {
237: dualViewer.setChecked(
238: requiredActivities[index], false);
239: handleActivityCheck(new HashSet(
240: Arrays.asList(dualViewer
241: .getCheckedElements())),
242: requiredActivities[index]);
243: }
244: }
245: }
246: }
247:
248: };
249:
250: protected CheckboxTreeViewer dualViewer;
251:
252: /**
253: * The Set of activities that belong to at least one category.
254: */
255: private Set managedActivities = new HashSet(7);
256:
257: /**
258: * The content provider.
259: */
260: protected ActivityCategoryContentProvider provider = new ActivityCategoryContentProvider();
261:
262: /**
263: * The descriptive text.
264: */
265: protected Text descriptionText;
266:
267: private Properties strings;
268:
269: private IMutableActivityManager activitySupport;
270:
271: /**
272: * Create a new instance.
273: *
274: * @param activitySupport
275: * the <code>IMutableActivityMananger</code> to use.
276: * @param strings
277: * map of strings to use. See the constants on
278: * {@link org.eclipse.ui.activities.ActivitiesPreferencePage} for
279: * details.
280: */
281: public ActivityEnabler(IMutableActivityManager activitySupport,
282: Properties strings) {
283: this .activitySupport = activitySupport;
284: this .strings = strings;
285: }
286:
287: /**
288: * Create the controls.
289: *
290: * @param parent
291: * the parent in which to create the controls.
292: * @return the composite in which the controls exist.
293: */
294: public Control createControl(Composite parent) {
295: GC gc = new GC(parent);
296: gc.setFont(JFaceResources.getDialogFont());
297: FontMetrics fontMetrics = gc.getFontMetrics();
298: gc.dispose();
299:
300: Composite composite = new Composite(parent, SWT.NONE);
301: composite.setLayout(createGridLayoutWithoutMargins(1,
302: fontMetrics));
303:
304: new Label(composite, SWT.NONE).setText(strings.getProperty(
305: ActivitiesPreferencePage.ACTIVITY_NAME,
306: ActivityMessages.ActivityEnabler_activities) + ':');
307:
308: dualViewer = new CheckboxTreeViewer(composite);
309: dualViewer.setComparator(new ViewerComparator());
310: dualViewer
311: .setLabelProvider(new ActivityCategoryLabelProvider());
312: dualViewer.setContentProvider(provider);
313: dualViewer.setInput(activitySupport);
314: GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
315: dualViewer.getControl().setLayoutData(data);
316:
317: Composite buttonComposite = new Composite(composite, SWT.NONE);
318: buttonComposite.setLayout(createGridLayoutWithoutMargins(2,
319: fontMetrics));
320:
321: Button selectAllButton = new Button(buttonComposite, SWT.PUSH);
322: selectAllButton
323: .setText(ActivityMessages.ActivityEnabler_selectAll);
324: selectAllButton.addSelectionListener(new SelectionAdapter() {
325: /*
326: * (non-Javadoc)
327: *
328: * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
329: */
330: public void widgetSelected(SelectionEvent e) {
331: toggleTreeEnablement(true);
332: }
333: });
334: setButtonLayoutData(selectAllButton, fontMetrics);
335:
336: Button deselectAllButton = new Button(buttonComposite, SWT.PUSH);
337: deselectAllButton
338: .setText(ActivityMessages.ActivityEnabler_deselectAll);
339: deselectAllButton.addSelectionListener(new SelectionAdapter() {
340: /*
341: * (non-Javadoc)
342: *
343: * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
344: */
345: public void widgetSelected(SelectionEvent e) {
346: toggleTreeEnablement(false);
347: }
348: });
349: setButtonLayoutData(deselectAllButton, fontMetrics);
350:
351: new Label(composite, SWT.NONE)
352: .setText(ActivityMessages.ActivityEnabler_description);
353:
354: descriptionText = new Text(composite, SWT.READ_ONLY | SWT.WRAP
355: | SWT.BORDER | SWT.V_SCROLL);
356: data = new GridData(SWT.FILL, SWT.FILL, true, false);
357: data.heightHint = Dialog.convertHeightInCharsToPixels(
358: fontMetrics, 5);
359: descriptionText.setLayoutData(data);
360: setInitialStates();
361:
362: dualViewer.addCheckStateListener(checkListener);
363: dualViewer.addSelectionChangedListener(selectionListener);
364:
365: dualViewer.setSelection(new StructuredSelection());
366:
367: Dialog.applyDialogFont(composite);
368:
369: return composite;
370: }
371:
372: private GridLayout createGridLayoutWithoutMargins(int nColumns,
373: FontMetrics fontMetrics) {
374: GridLayout layout = new GridLayout(nColumns, false);
375: layout.marginHeight = 0;
376: layout.marginWidth = 0;
377: layout.horizontalSpacing = Dialog
378: .convertHorizontalDLUsToPixels(fontMetrics,
379: IDialogConstants.HORIZONTAL_SPACING);
380: layout.verticalSpacing = Dialog.convertVerticalDLUsToPixels(
381: fontMetrics, IDialogConstants.VERTICAL_SPACING);
382: return layout;
383: }
384:
385: private GridData setButtonLayoutData(Button button,
386: FontMetrics fontMetrics) {
387: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
388: int widthHint = Dialog.convertHorizontalDLUsToPixels(
389: fontMetrics, IDialogConstants.BUTTON_WIDTH);
390: Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT,
391: true);
392: data.widthHint = Math.max(widthHint, minSize.x);
393: button.setLayoutData(data);
394: return data;
395: }
396:
397: /**
398: * @param categoryId
399: * the id to fetch.
400: * @return return all ids for activities that are in the given in the
401: * category.
402: */
403: private Collection getCategoryActivityIds(String categoryId) {
404: ICategory category = activitySupport.getCategory(categoryId);
405: Set activityBindings = category.getCategoryActivityBindings();
406: List categoryActivities = new ArrayList(activityBindings.size());
407: for (Iterator i = activityBindings.iterator(); i.hasNext();) {
408: ICategoryActivityBinding binding = (ICategoryActivityBinding) i
409: .next();
410: String activityId = binding.getActivityId();
411: categoryActivities.add(activityId);
412: }
413: return categoryActivities;
414: }
415:
416: /**
417: * Set the enabled category/activity check/grey states based on initial
418: * activity enablement.
419: */
420: private void setInitialStates() {
421: Set enabledActivities = activitySupport.getEnabledActivityIds();
422: setEnabledStates(enabledActivities);
423: }
424:
425: private void setEnabledStates(Set enabledActivities) {
426: Set categories = activitySupport.getDefinedCategoryIds();
427: List checked = new ArrayList(10), grayed = new ArrayList(10);
428: for (Iterator i = categories.iterator(); i.hasNext();) {
429: String categoryId = (String) i.next();
430: ICategory category = activitySupport
431: .getCategory(categoryId);
432:
433: int state = NONE;
434: Collection activities = getCategoryActivityIds(categoryId);
435: int foundCount = 0;
436: for (Iterator j = activities.iterator(); j.hasNext();) {
437: String activityId = (String) j.next();
438: managedActivities.add(activityId);
439: if (enabledActivities.contains(activityId)) {
440: IActivity activity = activitySupport
441: .getActivity(activityId);
442: checked.add(new CategorizedActivity(category,
443: activity));
444: //add activity proxy
445: foundCount++;
446: }
447: }
448:
449: if (foundCount == activities.size()) {
450: state = ALL;
451: } else if (foundCount > 0) {
452: state = SOME;
453: }
454:
455: if (state == NONE) {
456: continue;
457: }
458: checked.add(category);
459:
460: if (state == SOME) {
461: grayed.add(category);
462: }
463: }
464:
465: dualViewer.setCheckedElements(checked.toArray());
466: dualViewer.setGrayedElements(grayed.toArray());
467: }
468:
469: /**
470: * Update activity enablement based on the check states of activities in the
471: * tree.
472: */
473: public void updateActivityStates() {
474: Set enabledActivities = new HashSet(activitySupport
475: .getEnabledActivityIds());
476:
477: // remove all but the unmanaged activities (if any).
478: enabledActivities.removeAll(managedActivities);
479:
480: Object[] checked = dualViewer.getCheckedElements();
481: for (int i = 0; i < checked.length; i++) {
482: Object element = checked[i];
483: if (element instanceof ICategory
484: || dualViewer.getGrayed(element)) {
485: continue;
486: }
487: enabledActivities.add(((IActivity) element).getId());
488: }
489:
490: activitySupport.setEnabledActivityIds(enabledActivities);
491: }
492:
493: /**
494: * Restore the default activity states.
495: */
496: public void restoreDefaults() {
497: Set defaultEnabled = new HashSet();
498: Set activityIds = activitySupport.getDefinedActivityIds();
499: for (Iterator i = activityIds.iterator(); i.hasNext();) {
500: String activityId = (String) i.next();
501: IActivity activity = activitySupport
502: .getActivity(activityId);
503: try {
504: if (activity.isDefaultEnabled()) {
505: defaultEnabled.add(activityId);
506: }
507: } catch (NotDefinedException e) {
508: // this can't happen - we're iterating over defined activities.
509: }
510: }
511:
512: setEnabledStates(defaultEnabled);
513: }
514:
515: /**
516: * Toggles the enablement state of all activities.
517: *
518: * @param enabled
519: * whether the tree should be enabled
520: */
521: protected void toggleTreeEnablement(boolean enabled) {
522: Object[] elements = provider.getElements(activitySupport);
523:
524: //reset grey state to null
525: dualViewer.setGrayedElements(new Object[0]);
526:
527: //enable all categories
528: for (int i = 0; i < elements.length; i++) {
529: dualViewer.expandToLevel(elements[i],
530: AbstractTreeViewer.ALL_LEVELS);
531: dualViewer.setSubtreeChecked(elements[i], enabled);
532: }
533: }
534: }
|