001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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: * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font
011: * should be activated and used by other components.
012: *******************************************************************************/package org.eclipse.ui.internal.ide.dialogs;
013:
014: import java.util.ArrayList;
015: import java.util.List;
016:
017: import org.eclipse.core.resources.IContainer;
018: import org.eclipse.core.resources.IProject;
019: import org.eclipse.core.resources.IResource;
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.IAdaptable;
022: import org.eclipse.core.runtime.IPath;
023: import org.eclipse.core.runtime.IStatus;
024: import org.eclipse.jface.dialogs.Dialog;
025: import org.eclipse.jface.dialogs.ErrorDialog;
026: import org.eclipse.jface.dialogs.IDialogConstants;
027: import org.eclipse.jface.dialogs.MessageDialog;
028: import org.eclipse.jface.viewers.CheckStateChangedEvent;
029: import org.eclipse.jface.viewers.CheckboxTreeViewer;
030: import org.eclipse.jface.viewers.DecoratingLabelProvider;
031: import org.eclipse.jface.viewers.ICheckStateListener;
032: import org.eclipse.jface.viewers.ISelection;
033: import org.eclipse.jface.viewers.IStructuredSelection;
034: import org.eclipse.jface.viewers.ITreeContentProvider;
035: import org.eclipse.jface.viewers.ITreeViewerListener;
036: import org.eclipse.jface.viewers.TreeExpansionEvent;
037: import org.eclipse.jface.wizard.WizardPage;
038: import org.eclipse.swt.SWT;
039: import org.eclipse.swt.custom.BusyIndicator;
040: import org.eclipse.swt.events.ModifyEvent;
041: import org.eclipse.swt.events.ModifyListener;
042: import org.eclipse.swt.events.SelectionAdapter;
043: import org.eclipse.swt.events.SelectionEvent;
044: import org.eclipse.swt.layout.GridData;
045: import org.eclipse.swt.layout.GridLayout;
046: import org.eclipse.swt.widgets.Button;
047: import org.eclipse.swt.widgets.Composite;
048: import org.eclipse.swt.widgets.Label;
049: import org.eclipse.swt.widgets.Shell;
050: import org.eclipse.swt.widgets.Text;
051: import org.eclipse.ui.IWorkbenchPage;
052: import org.eclipse.ui.IWorkbenchPart;
053: import org.eclipse.ui.IWorkingSet;
054: import org.eclipse.ui.IWorkingSetManager;
055: import org.eclipse.ui.PlatformUI;
056: import org.eclipse.ui.dialogs.IWorkingSetPage;
057: import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;
058: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
059: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
060: import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
061: import org.eclipse.ui.model.WorkbenchContentProvider;
062: import org.eclipse.ui.model.WorkbenchLabelProvider;
063:
064: /**
065: * A resource working set page allows the user to edit an
066: * existing working set and create a new working set.
067: * <p>
068: * Working set elements are presented as a simple resource tree.
069: * </p>
070: *
071: * @since 2.0
072: */
073: public class ResourceWorkingSetPage extends WizardPage implements
074: IWorkingSetPage {
075: private final static int SIZING_SELECTION_WIDGET_WIDTH = 50;
076:
077: private final static int SIZING_SELECTION_WIDGET_HEIGHT = 200;
078:
079: private Text text;
080:
081: private CheckboxTreeViewer tree;
082:
083: private IWorkingSet workingSet;
084:
085: private boolean firstCheck = false; // set to true if selection is set in setSelection
086:
087: /**
088: * Creates a new instance of the receiver.
089: */
090: public ResourceWorkingSetPage() {
091: super (
092: "resourceWorkingSetPage", //$NON-NLS-1$
093: IDEWorkbenchMessages.ResourceWorkingSetPage_title,
094: IDEInternalWorkbenchImages
095: .getImageDescriptor(IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ));
096: setDescription(IDEWorkbenchMessages.ResourceWorkingSetPage_description);
097: }
098:
099: /**
100: * Adds working set elements contained in the given container to the list
101: * of checked resources.
102: *
103: * @param collectedResources list of collected resources
104: * @param container container to collect working set elements for
105: */
106: private void addWorkingSetElements(List collectedResources,
107: IContainer container) {
108: IAdaptable[] elements = workingSet.getElements();
109: IPath containerPath = container.getFullPath();
110:
111: for (int i = 0; i < elements.length; i++) {
112: IResource resource = null;
113:
114: if (elements[i] instanceof IResource) {
115: resource = (IResource) elements[i];
116: } else {
117: resource = (IResource) elements[i]
118: .getAdapter(IResource.class);
119: }
120:
121: if (resource != null) {
122: IPath resourcePath = resource.getFullPath();
123: if (containerPath.isPrefixOf(resourcePath)) {
124: collectedResources.add(elements[i]);
125: }
126: }
127: }
128: }
129:
130: /**
131: * Overrides method in WizardPage.
132: *
133: * @see org.eclipse.jface.wizard.WizardPage#createControl(Composite)
134: */
135: public void createControl(Composite parent) {
136: initializeDialogUnits(parent);
137:
138: Composite composite = new Composite(parent, SWT.NULL);
139: composite.setLayout(new GridLayout());
140: composite.setLayoutData(new GridData(
141: GridData.HORIZONTAL_ALIGN_FILL));
142: setControl(composite);
143:
144: PlatformUI.getWorkbench().getHelpSystem().setHelp(composite,
145: IIDEHelpContextIds.WORKING_SET_RESOURCE_PAGE);
146: Label label = new Label(composite, SWT.WRAP);
147: label
148: .setText(IDEWorkbenchMessages.ResourceWorkingSetPage_message);
149: GridData data = new GridData(GridData.GRAB_HORIZONTAL
150: | GridData.HORIZONTAL_ALIGN_FILL
151: | GridData.VERTICAL_ALIGN_CENTER);
152: label.setLayoutData(data);
153:
154: text = new Text(composite, SWT.SINGLE | SWT.BORDER);
155: text.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
156: | GridData.HORIZONTAL_ALIGN_FILL));
157: text.addModifyListener(new ModifyListener() {
158: public void modifyText(ModifyEvent e) {
159: validateInput();
160: }
161: });
162: text.setFocus();
163:
164: label = new Label(composite, SWT.WRAP);
165: label
166: .setText(IDEWorkbenchMessages.ResourceWorkingSetPage_label_tree);
167: data = new GridData(GridData.GRAB_HORIZONTAL
168: | GridData.HORIZONTAL_ALIGN_FILL
169: | GridData.VERTICAL_ALIGN_CENTER);
170: label.setLayoutData(data);
171:
172: tree = new CheckboxTreeViewer(composite);
173: tree.setUseHashlookup(true);
174: final ITreeContentProvider treeContentProvider = new WorkbenchContentProvider();
175: tree.setContentProvider(treeContentProvider);
176: tree.setLabelProvider(new DecoratingLabelProvider(
177: new WorkbenchLabelProvider(), IDEWorkbenchPlugin
178: .getDefault().getWorkbench()
179: .getDecoratorManager().getLabelDecorator()));
180: tree
181: .setInput(IDEWorkbenchPlugin.getPluginWorkspace()
182: .getRoot());
183: tree.setComparator(new ResourceComparator(
184: ResourceComparator.NAME));
185:
186: data = new GridData(GridData.FILL_BOTH | GridData.GRAB_VERTICAL);
187: data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT;
188: data.widthHint = SIZING_SELECTION_WIDGET_WIDTH;
189: tree.getControl().setLayoutData(data);
190:
191: tree.addCheckStateListener(new ICheckStateListener() {
192: public void checkStateChanged(CheckStateChangedEvent event) {
193: handleCheckStateChange(event);
194: }
195: });
196:
197: tree.addTreeListener(new ITreeViewerListener() {
198: public void treeCollapsed(TreeExpansionEvent event) {
199: }
200:
201: public void treeExpanded(TreeExpansionEvent event) {
202: final Object element = event.getElement();
203: if (tree.getGrayed(element) == false) {
204: BusyIndicator.showWhile(getShell().getDisplay(),
205: new Runnable() {
206: public void run() {
207: setSubtreeChecked(
208: (IContainer) element,
209: tree.getChecked(element),
210: false);
211: }
212: });
213: }
214: }
215: });
216:
217: // Add select / deselect all buttons for bug 46669
218: Composite buttonComposite = new Composite(composite, SWT.NONE);
219: GridLayout layout = new GridLayout(2, false);
220: layout.marginWidth = 0;
221: layout.marginHeight = 0;
222: layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
223: buttonComposite.setLayout(layout);
224: buttonComposite.setLayoutData(new GridData(
225: GridData.HORIZONTAL_ALIGN_FILL));
226:
227: Button selectAllButton = new Button(buttonComposite, SWT.PUSH);
228: selectAllButton
229: .setText(IDEWorkbenchMessages.ResourceWorkingSetPage_selectAll_label);
230: selectAllButton
231: .setToolTipText(IDEWorkbenchMessages.ResourceWorkingSetPage_selectAll_toolTip);
232: selectAllButton.addSelectionListener(new SelectionAdapter() {
233: public void widgetSelected(SelectionEvent selectionEvent) {
234: tree.setCheckedElements(treeContentProvider
235: .getElements(tree.getInput()));
236: validateInput();
237: }
238: });
239: setButtonLayoutData(selectAllButton);
240:
241: Button deselectAllButton = new Button(buttonComposite, SWT.PUSH);
242: deselectAllButton
243: .setText(IDEWorkbenchMessages.ResourceWorkingSetPage_deselectAll_label);
244: deselectAllButton
245: .setToolTipText(IDEWorkbenchMessages.ResourceWorkingSetPage_deselectAll_toolTip);
246: deselectAllButton.addSelectionListener(new SelectionAdapter() {
247: public void widgetSelected(SelectionEvent selectionEvent) {
248: tree.setCheckedElements(new Object[0]);
249: validateInput();
250: }
251: });
252: setButtonLayoutData(deselectAllButton);
253:
254: initializeCheckedState();
255: if (workingSet != null) {
256: text.setText(workingSet.getName());
257: }
258: setPageComplete(false);
259:
260: Dialog.applyDialogFont(composite);
261: }
262:
263: /**
264: * Collects all checked resources in the specified container.
265: *
266: * @param checkedResources the output, list of checked resources
267: * @param container the container to collect checked resources in
268: */
269: private void findCheckedResources(List checkedResources,
270: IContainer container) {
271: IResource[] resources = null;
272: try {
273: resources = container.members();
274: } catch (CoreException ex) {
275: handleCoreException(
276: ex,
277: getShell(),
278: IDEWorkbenchMessages.ResourceWorkingSetPage_error,
279: IDEWorkbenchMessages.ResourceWorkingSetPage_error_updateCheckedState);
280: }
281: for (int i = 0; i < resources.length; i++) {
282: if (tree.getGrayed(resources[i])) {
283: if (resources[i].isAccessible()) {
284: findCheckedResources(checkedResources,
285: (IContainer) resources[i]);
286: } else {
287: addWorkingSetElements(checkedResources,
288: (IContainer) resources[i]);
289: }
290: } else if (tree.getChecked(resources[i])) {
291: checkedResources.add(resources[i]);
292: }
293: }
294: }
295:
296: /**
297: * Implements IWorkingSetPage.
298: *
299: * @see org.eclipse.ui.dialogs.IWorkingSetPage#finish()
300: */
301: public void finish() {
302: ArrayList resources = new ArrayList(10);
303: findCheckedResources(resources, (IContainer) tree.getInput());
304: if (workingSet == null) {
305: IWorkingSetManager workingSetManager = PlatformUI
306: .getWorkbench().getWorkingSetManager();
307: workingSet = workingSetManager.createWorkingSet(
308: getWorkingSetName(), (IAdaptable[]) resources
309: .toArray(new IAdaptable[resources.size()]));
310: } else {
311: workingSet.setName(getWorkingSetName());
312: workingSet.setElements((IAdaptable[]) resources
313: .toArray(new IAdaptable[resources.size()]));
314: }
315: }
316:
317: /**
318: * Implements IWorkingSetPage.
319: *
320: * @see org.eclipse.ui.dialogs.IWorkingSetPage#getSelection()
321: */
322: public IWorkingSet getSelection() {
323: return workingSet;
324: }
325:
326: /**
327: * Returns the name entered in the working set name field.
328: *
329: * @return the name entered in the working set name field.
330: */
331: private String getWorkingSetName() {
332: return text.getText();
333: }
334:
335: /**
336: * Called when the checked state of a tree item changes.
337: *
338: * @param event the checked state change event.
339: */
340: private void handleCheckStateChange(
341: final CheckStateChangedEvent event) {
342: BusyIndicator.showWhile(getShell().getDisplay(),
343: new Runnable() {
344: public void run() {
345: IResource resource = (IResource) event
346: .getElement();
347: boolean state = event.getChecked();
348:
349: tree.setGrayed(resource, false);
350: if (resource instanceof IContainer) {
351: setSubtreeChecked((IContainer) resource,
352: state, true);
353: }
354: updateParentState(resource);
355: validateInput();
356: }
357: });
358: }
359:
360: /**
361: * Displays an error message when a CoreException occured.
362: *
363: * @param exception the CoreException
364: * @param shell parent shell for the message box
365: * @param title the mesage box title
366: * @param message additional error message
367: */
368: private void handleCoreException(CoreException exception,
369: Shell shell, String title, String message) {
370: IStatus status = exception.getStatus();
371: if (status != null) {
372: ErrorDialog.openError(shell, title, message, status);
373: } else {
374: MessageDialog.openError(shell,
375: IDEWorkbenchMessages.InternalError, exception
376: .getLocalizedMessage());
377: }
378: }
379:
380: /**
381: * Sets the checked state of tree items based on the initial
382: * working set, if any.
383: */
384: private void initializeCheckedState() {
385: BusyIndicator.showWhile(getShell().getDisplay(),
386: new Runnable() {
387: public void run() {
388: Object[] items = null;
389: if (workingSet == null) {
390:
391: IWorkbenchPage page = IDEWorkbenchPlugin
392: .getDefault().getWorkbench()
393: .getActiveWorkbenchWindow()
394: .getActivePage();
395: if (page == null) {
396: return;
397: }
398: IWorkbenchPart part = page.getActivePart();
399: if (part == null) {
400: return;
401: }
402: ISelection selection = page.getSelection();
403: if (selection instanceof IStructuredSelection) {
404: items = ((IStructuredSelection) selection)
405: .toArray();
406: }
407:
408: } else {
409: items = workingSet.getElements();
410: }
411: if (items == null) {
412: return;
413: }
414: tree.setCheckedElements(items);
415: for (int i = 0; i < items.length; i++) {
416: IAdaptable item = null;
417: if (!(items[i] instanceof IAdaptable)) {
418: continue;
419: }
420: item = (IAdaptable) items[i];
421: IContainer container = null;
422: IResource resource = null;
423:
424: if (item instanceof IContainer) {
425: container = (IContainer) item;
426: } else {
427: container = (IContainer) item
428: .getAdapter(IContainer.class);
429: }
430: if (container != null) {
431: setSubtreeChecked(container, true, true);
432: }
433: if (item instanceof IResource) {
434: resource = (IResource) item;
435: } else {
436: resource = (IResource) item
437: .getAdapter(IResource.class);
438: }
439: if (resource != null
440: && resource.isAccessible() == false) {
441: IProject project = resource
442: .getProject();
443: if (tree.getChecked(project) == false) {
444: tree.setGrayChecked(project, true);
445: }
446: } else {
447: updateParentState(resource);
448: }
449: }
450: }
451: });
452: }
453:
454: /**
455: * Implements IWorkingSetPage.
456: *
457: * @see org.eclipse.ui.dialogs.IWorkingSetPage#setSelection(IWorkingSet)
458: */
459: public void setSelection(IWorkingSet workingSet) {
460: if (workingSet == null) {
461: throw new IllegalArgumentException(
462: "Working set must not be null"); //$NON-NLS-1$
463: }
464: this .workingSet = workingSet;
465: if (getShell() != null && text != null) {
466: firstCheck = true;
467: initializeCheckedState();
468: text.setText(workingSet.getName());
469: }
470: }
471:
472: /**
473: * Sets the checked state of the container's members.
474: *
475: * @param container the container whose children should be checked/unchecked
476: * @param state true=check all members in the container. false=uncheck all
477: * members in the container.
478: * @param checkExpandedState true=recurse into sub-containers and set the
479: * checked state. false=only set checked state of members of this container
480: */
481: private void setSubtreeChecked(IContainer container, boolean state,
482: boolean checkExpandedState) {
483: // checked state is set lazily on expand, don't set it if container is collapsed
484: if (container.isAccessible() == false
485: || (tree.getExpandedState(container) == false && state && checkExpandedState)) {
486: return;
487: }
488: IResource[] members = null;
489: try {
490: members = container.members();
491: } catch (CoreException ex) {
492: handleCoreException(
493: ex,
494: getShell(),
495: IDEWorkbenchMessages.ResourceWorkingSetPage_error,
496: IDEWorkbenchMessages.ResourceWorkingSetPage_error_updateCheckedState);
497: }
498: for (int i = members.length - 1; i >= 0; i--) {
499: IResource element = members[i];
500: boolean elementGrayChecked = tree.getGrayed(element)
501: || tree.getChecked(element);
502:
503: if (state) {
504: tree.setChecked(element, true);
505: tree.setGrayed(element, false);
506: } else {
507: tree.setGrayChecked(element, false);
508: }
509: // unchecked state only needs to be set when the container is
510: // checked or grayed
511: if (element instanceof IContainer
512: && (state || elementGrayChecked)) {
513: setSubtreeChecked((IContainer) element, state, true);
514: }
515: }
516: }
517:
518: /**
519: * Check and gray the resource parent if all resources of the
520: * parent are checked.
521: *
522: * @param child the resource whose parent checked state should
523: * be set.
524: */
525: private void updateParentState(IResource child) {
526: if (child == null || child.getParent() == null) {
527: return;
528: }
529:
530: IContainer parent = child.getParent();
531: boolean childChecked = false;
532: IResource[] members = null;
533: try {
534: members = parent.members();
535: } catch (CoreException ex) {
536: handleCoreException(
537: ex,
538: getShell(),
539: IDEWorkbenchMessages.ResourceWorkingSetPage_error,
540: IDEWorkbenchMessages.ResourceWorkingSetPage_error_updateCheckedState);
541: }
542: for (int i = members.length - 1; i >= 0; i--) {
543: if (tree.getChecked(members[i])
544: || tree.getGrayed(members[i])) {
545: childChecked = true;
546: break;
547: }
548: }
549: tree.setGrayChecked(parent, childChecked);
550: updateParentState(parent);
551: }
552:
553: /**
554: * Validates the working set name and the checked state of the
555: * resource tree.
556: */
557: private void validateInput() {
558: String errorMessage = null;
559: String infoMessage = null;
560: String newText = text.getText();
561:
562: if (newText.equals(newText.trim()) == false) {
563: errorMessage = IDEWorkbenchMessages.ResourceWorkingSetPage_warning_nameWhitespace;
564: } else if (firstCheck) {
565: firstCheck = false;
566: return;
567: }
568: if (newText.equals("")) { //$NON-NLS-1$
569: errorMessage = IDEWorkbenchMessages.ResourceWorkingSetPage_warning_nameMustNotBeEmpty;
570: }
571: if (errorMessage == null
572: && (workingSet == null || newText.equals(workingSet
573: .getName()) == false)) {
574: IWorkingSet[] workingSets = PlatformUI.getWorkbench()
575: .getWorkingSetManager().getWorkingSets();
576: for (int i = 0; i < workingSets.length; i++) {
577: if (newText.equals(workingSets[i].getName())) {
578: errorMessage = IDEWorkbenchMessages.ResourceWorkingSetPage_warning_workingSetExists;
579: }
580: }
581: }
582: if (infoMessage == null
583: && tree.getCheckedElements().length == 0) {
584: infoMessage = IDEWorkbenchMessages.ResourceWorkingSetPage_warning_resourceMustBeChecked;
585: }
586: setMessage(infoMessage, INFORMATION);
587: setErrorMessage(errorMessage);
588: setPageComplete(errorMessage == null);
589: }
590: }
|