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: *******************************************************************************/package org.eclipse.ui.dialogs;
011:
012: import java.util.ArrayList;
013: import java.util.Hashtable;
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.Map;
017: import java.util.Vector;
018:
019: import org.eclipse.core.resources.IContainer;
020: import org.eclipse.core.resources.IProject;
021: import org.eclipse.core.resources.IResource;
022: import org.eclipse.core.resources.ResourcesPlugin;
023: import org.eclipse.core.runtime.CoreException;
024: import org.eclipse.jface.dialogs.IDialogConstants;
025: import org.eclipse.jface.viewers.CheckStateChangedEvent;
026: import org.eclipse.jface.viewers.ICheckStateListener;
027: import org.eclipse.jface.viewers.IStructuredSelection;
028: import org.eclipse.jface.viewers.ITreeContentProvider;
029: import org.eclipse.swt.SWT;
030: import org.eclipse.swt.custom.BusyIndicator;
031: import org.eclipse.swt.events.SelectionAdapter;
032: import org.eclipse.swt.events.SelectionEvent;
033: import org.eclipse.swt.events.SelectionListener;
034: import org.eclipse.swt.graphics.Font;
035: import org.eclipse.swt.layout.GridData;
036: import org.eclipse.swt.layout.GridLayout;
037: import org.eclipse.swt.widgets.Button;
038: import org.eclipse.swt.widgets.Composite;
039: import org.eclipse.swt.widgets.Shell;
040: import org.eclipse.ui.ide.IDE;
041: import org.eclipse.ui.internal.ide.DialogUtil;
042: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
043: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
044: import org.eclipse.ui.internal.ide.dialogs.ResourceTreeAndListGroup;
045: import org.eclipse.ui.model.WorkbenchContentProvider;
046: import org.eclipse.ui.model.WorkbenchLabelProvider;
047:
048: /**
049: * Abstract superclass for a typical export wizard's main page.
050: * <p>
051: * Clients may subclass this page to inherit its common destination resource
052: * selection facilities.
053: * </p>
054: * <p>
055: * Subclasses must implement
056: * <ul>
057: * <li><code>createDestinationGroup</code></li>
058: * </ul>
059: * </p>
060: * <p>
061: * Subclasses may override
062: * <ul>
063: * <li><code>allowNewContainerName</code></li>
064: * </ul>
065: * </p>
066: * <p>
067: * Subclasses may extend
068: * <ul>
069: * <li><code>handleEvent</code></li>
070: * <li><code>internalSaveWidgetValues</code></li>
071: * <li><code>updateWidgetEnablements</code></li>
072: * </ul>
073: * </p>
074: */
075: public abstract class WizardExportResourcesPage extends
076: WizardDataTransferPage {
077: private IStructuredSelection initialResourceSelection;
078:
079: private List selectedTypes = new ArrayList();
080:
081: // widgets
082: private ResourceTreeAndListGroup resourceGroup;
083:
084: private final static String SELECT_TYPES_TITLE = IDEWorkbenchMessages.WizardTransferPage_selectTypes;
085:
086: private final static String SELECT_ALL_TITLE = IDEWorkbenchMessages.WizardTransferPage_selectAll;
087:
088: private final static String DESELECT_ALL_TITLE = IDEWorkbenchMessages.WizardTransferPage_deselectAll;
089:
090: /**
091: * Creates an export wizard page. If the current resource selection
092: * is not empty then it will be used as the initial collection of resources
093: * selected for export.
094: *
095: * @param pageName the name of the page
096: * @param selection {@link IStructuredSelection} of {@link IResource}
097: * @see IDE#computeSelectedResources(IStructuredSelection)
098: */
099: protected WizardExportResourcesPage(String pageName,
100: IStructuredSelection selection) {
101: super (pageName);
102: this .initialResourceSelection = selection;
103: }
104:
105: /**
106: * The <code>addToHierarchyToCheckedStore</code> implementation of this
107: * <code>WizardDataTransferPage</code> method returns <code>false</code>.
108: * Subclasses may override this method.
109: */
110: protected boolean allowNewContainerName() {
111: return false;
112: }
113:
114: /**
115: * Creates a new button with the given id.
116: * <p>
117: * The <code>Dialog</code> implementation of this framework method
118: * creates a standard push button, registers for selection events
119: * including button presses and registers
120: * default buttons with its shell.
121: * The button id is stored as the buttons client data.
122: * Note that the parent's layout is assumed to be a GridLayout and
123: * the number of columns in this layout is incremented.
124: * Subclasses may override.
125: * </p>
126: *
127: * @param parent the parent composite
128: * @param id the id of the button (see
129: * <code>IDialogConstants.*_ID</code> constants
130: * for standard dialog button ids)
131: * @param label the label from the button
132: * @param defaultButton <code>true</code> if the button is to be the
133: * default button, and <code>false</code> otherwise
134: */
135: protected Button createButton(Composite parent, int id,
136: String label, boolean defaultButton) {
137: // increment the number of columns in the button bar
138: ((GridLayout) parent.getLayout()).numColumns++;
139:
140: Button button = new Button(parent, SWT.PUSH);
141:
142: GridData buttonData = new GridData(GridData.FILL_HORIZONTAL);
143: button.setLayoutData(buttonData);
144:
145: button.setData(new Integer(id));
146: button.setText(label);
147: button.setFont(parent.getFont());
148:
149: if (defaultButton) {
150: Shell shell = parent.getShell();
151: if (shell != null) {
152: shell.setDefaultButton(button);
153: }
154: button.setFocus();
155: }
156: button.setFont(parent.getFont());
157: setButtonLayoutData(button);
158: return button;
159: }
160:
161: /**
162: * Creates the buttons for selecting specific types or selecting all or none of the
163: * elements.
164: *
165: * @param parent the parent control
166: */
167: protected final void createButtonsGroup(Composite parent) {
168:
169: Font font = parent.getFont();
170:
171: // top level group
172: Composite buttonComposite = new Composite(parent, SWT.NONE);
173: buttonComposite.setFont(parent.getFont());
174:
175: GridLayout layout = new GridLayout();
176: layout.numColumns = 3;
177: layout.makeColumnsEqualWidth = true;
178: buttonComposite.setLayout(layout);
179: buttonComposite.setLayoutData(new GridData(
180: GridData.VERTICAL_ALIGN_FILL
181: | GridData.HORIZONTAL_ALIGN_FILL));
182:
183: // types edit button
184: Button selectTypesButton = createButton(buttonComposite,
185: IDialogConstants.SELECT_TYPES_ID, SELECT_TYPES_TITLE,
186: false);
187:
188: SelectionListener listener = new SelectionAdapter() {
189: public void widgetSelected(SelectionEvent e) {
190: handleTypesEditButtonPressed();
191: }
192: };
193: selectTypesButton.addSelectionListener(listener);
194: selectTypesButton.setFont(font);
195: setButtonLayoutData(selectTypesButton);
196:
197: Button selectButton = createButton(buttonComposite,
198: IDialogConstants.SELECT_ALL_ID, SELECT_ALL_TITLE, false);
199:
200: listener = new SelectionAdapter() {
201: public void widgetSelected(SelectionEvent e) {
202: resourceGroup.setAllSelections(true);
203: }
204: };
205: selectButton.addSelectionListener(listener);
206: selectButton.setFont(font);
207: setButtonLayoutData(selectButton);
208:
209: Button deselectButton = createButton(buttonComposite,
210: IDialogConstants.DESELECT_ALL_ID, DESELECT_ALL_TITLE,
211: false);
212:
213: listener = new SelectionAdapter() {
214: public void widgetSelected(SelectionEvent e) {
215: resourceGroup.setAllSelections(false);
216: }
217: };
218: deselectButton.addSelectionListener(listener);
219: deselectButton.setFont(font);
220: setButtonLayoutData(deselectButton);
221:
222: }
223:
224: /** (non-Javadoc)
225: * Method declared on IDialogPage.
226: */
227: public void createControl(Composite parent) {
228:
229: initializeDialogUnits(parent);
230:
231: Composite composite = new Composite(parent, SWT.NULL);
232: composite.setLayout(new GridLayout());
233: composite.setLayoutData(new GridData(
234: GridData.VERTICAL_ALIGN_FILL
235: | GridData.HORIZONTAL_ALIGN_FILL));
236: composite.setFont(parent.getFont());
237:
238: createResourcesGroup(composite);
239: createButtonsGroup(composite);
240:
241: createDestinationGroup(composite);
242:
243: createOptionsGroup(composite);
244:
245: restoreResourceSpecificationWidgetValues(); // ie.- local
246: restoreWidgetValues(); // ie.- subclass hook
247: if (initialResourceSelection != null) {
248: setupBasedOnInitialSelections();
249: }
250:
251: updateWidgetEnablements();
252: setPageComplete(determinePageCompletion());
253: setErrorMessage(null); // should not initially have error message
254:
255: setControl(composite);
256: }
257:
258: /**
259: * Creates the export destination specification visual components.
260: * <p>
261: * Subclasses must implement this method.
262: * </p>
263: *
264: * @param parent the parent control
265: */
266: protected abstract void createDestinationGroup(Composite parent);
267:
268: /**
269: * Creates the checkbox tree and list for selecting resources.
270: *
271: * @param parent the parent control
272: */
273: protected final void createResourcesGroup(Composite parent) {
274:
275: //create the input element, which has the root resource
276: //as its only child
277: List input = new ArrayList();
278: IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
279: .getProjects();
280: for (int i = 0; i < projects.length; i++) {
281: if (projects[i].isOpen()) {
282: input.add(projects[i]);
283: }
284: }
285:
286: this .resourceGroup = new ResourceTreeAndListGroup(parent,
287: input, getResourceProvider(IResource.FOLDER
288: | IResource.PROJECT), WorkbenchLabelProvider
289: .getDecoratingWorkbenchLabelProvider(),
290: getResourceProvider(IResource.FILE),
291: WorkbenchLabelProvider
292: .getDecoratingWorkbenchLabelProvider(),
293: SWT.NONE, DialogUtil.inRegularFontMode(parent));
294:
295: ICheckStateListener listener = new ICheckStateListener() {
296: public void checkStateChanged(CheckStateChangedEvent event) {
297: updateWidgetEnablements();
298: }
299: };
300:
301: this .resourceGroup.addCheckStateListener(listener);
302: }
303:
304: /*
305: * @see WizardDataTransferPage.getErrorDialogTitle()
306: */
307: protected String getErrorDialogTitle() {
308: return IDEWorkbenchMessages.WizardExportPage_errorDialogTitle;
309: }
310:
311: /**
312: * Obsolete method. This was implemented to handle the case where ensureLocal()
313: * needed to be called but it doesn't use it any longer.
314: *
315: * @deprecated Only retained for backwards compatibility.
316: */
317: protected boolean ensureResourcesLocal(List resources) {
318: return true;
319: }
320:
321: /**
322: * Returns a new subcollection containing only those resources which are not
323: * local.
324: *
325: * @param originalList the original list of resources (element type:
326: * <code>IResource</code>)
327: * @return the new list of non-local resources (element type:
328: * <code>IResource</code>)
329: */
330: protected List extractNonLocalResources(List originalList) {
331: Vector result = new Vector(originalList.size());
332: Iterator resourcesEnum = originalList.iterator();
333:
334: while (resourcesEnum.hasNext()) {
335: IResource currentResource = (IResource) resourcesEnum
336: .next();
337: if (!currentResource.isLocal(IResource.DEPTH_ZERO)) {
338: result.addElement(currentResource);
339: }
340: }
341:
342: return result;
343: }
344:
345: /**
346: * Returns a content provider for <code>IResource</code>s that returns
347: * only children of the given resource type.
348: */
349: private ITreeContentProvider getResourceProvider(
350: final int resourceType) {
351: return new WorkbenchContentProvider() {
352: public Object[] getChildren(Object o) {
353: if (o instanceof IContainer) {
354: IResource[] members = null;
355: try {
356: members = ((IContainer) o).members();
357: } catch (CoreException e) {
358: //just return an empty set of children
359: return new Object[0];
360: }
361:
362: //filter out the desired resource types
363: ArrayList results = new ArrayList();
364: for (int i = 0; i < members.length; i++) {
365: //And the test bits with the resource types to see if they are what we want
366: if ((members[i].getType() & resourceType) > 0) {
367: results.add(members[i]);
368: }
369: }
370: return results.toArray();
371: }
372: //input element case
373: if (o instanceof ArrayList) {
374: return ((ArrayList) o).toArray();
375: }
376: return new Object[0];
377: }
378: };
379: }
380:
381: /**
382: * Returns this page's collection of currently-specified resources to be
383: * exported. This is the primary resource selection facility accessor for
384: * subclasses.
385: *
386: * @return a collection of resources currently selected
387: * for export (element type: <code>IResource</code>)
388: */
389: protected List getSelectedResources() {
390: Iterator resourcesToExportIterator = this
391: .getSelectedResourcesIterator();
392: List resourcesToExport = new ArrayList();
393: while (resourcesToExportIterator.hasNext()) {
394: resourcesToExport.add(resourcesToExportIterator.next());
395: }
396: return resourcesToExport;
397: }
398:
399: /**
400: * Returns this page's collection of currently-specified resources to be
401: * exported. This is the primary resource selection facility accessor for
402: * subclasses.
403: *
404: * @return an iterator over the collection of resources currently selected
405: * for export (element type: <code>IResource</code>). This will include
406: * white checked folders and individually checked files.
407: */
408: protected Iterator getSelectedResourcesIterator() {
409: return this .resourceGroup.getAllCheckedListItems().iterator();
410: }
411:
412: /**
413: * Returns the resource extensions currently specified to be exported.
414: *
415: * @return the resource extensions currently specified to be exported (element
416: * type: <code>String</code>)
417: */
418: protected List getTypesToExport() {
419:
420: return selectedTypes;
421: }
422:
423: /**
424: * Returns this page's collection of currently-specified resources to be
425: * exported. This returns both folders and files - for just the files use
426: * getSelectedResources.
427: *
428: * @return a collection of resources currently selected
429: * for export (element type: <code>IResource</code>)
430: */
431: protected List getWhiteCheckedResources() {
432:
433: return this .resourceGroup.getAllWhiteCheckedItems();
434: }
435:
436: /**
437: * Queries the user for the types of resources to be exported and selects
438: * them in the checkbox group.
439: */
440: protected void handleTypesEditButtonPressed() {
441: Object[] newSelectedTypes = queryResourceTypesToExport();
442:
443: if (newSelectedTypes != null) { // ie.- did not press Cancel
444: this .selectedTypes = new ArrayList(newSelectedTypes.length);
445: for (int i = 0; i < newSelectedTypes.length; i++) {
446: this .selectedTypes.add(newSelectedTypes[i]);
447: }
448: setupSelectionsBasedOnSelectedTypes();
449: }
450:
451: }
452:
453: /**
454: * Returns whether the extension of the given resource name is an extension that
455: * has been specified for export by the user.
456: *
457: * @param resourceName the resource name
458: * @return <code>true</code> if the resource name is suitable for export based
459: * upon its extension
460: */
461: protected boolean hasExportableExtension(String resourceName) {
462: if (selectedTypes == null) {
463: return true;
464: }
465:
466: int separatorIndex = resourceName.lastIndexOf("."); //$NON-NLS-1$
467: if (separatorIndex == -1) {
468: return false;
469: }
470:
471: String extension = resourceName.substring(separatorIndex + 1);
472:
473: Iterator it = selectedTypes.iterator();
474: while (it.hasNext()) {
475: if (extension.equalsIgnoreCase((String) it.next())) {
476: return true;
477: }
478: }
479:
480: return false;
481: }
482:
483: /**
484: * Persists additional setting that are to be restored in the next instance of
485: * this page.
486: * <p>
487: * The <code>WizardImportPage</code> implementation of this method does
488: * nothing. Subclasses may extend to persist additional settings.
489: * </p>
490: */
491: protected void internalSaveWidgetValues() {
492: }
493:
494: /**
495: * Queries the user for the resource types that are to be exported and returns
496: * these types as an array.
497: *
498: * @return the resource types selected for export (element type:
499: * <code>String</code>), or <code>null</code> if the user canceled the
500: * selection
501: */
502: protected Object[] queryResourceTypesToExport() {
503:
504: TypeFilteringDialog dialog = new TypeFilteringDialog(
505: getContainer().getShell(), getTypesToExport());
506:
507: dialog.open();
508:
509: return dialog.getResult();
510: }
511:
512: /**
513: * Restores resource specification control settings that were persisted
514: * in the previous instance of this page. Subclasses wishing to restore
515: * persisted values for their controls may extend.
516: */
517: protected void restoreResourceSpecificationWidgetValues() {
518: }
519:
520: /**
521: * Persists resource specification control setting that are to be restored
522: * in the next instance of this page. Subclasses wishing to persist additional
523: * setting for their controls should extend hook method
524: * <code>internalSaveWidgetValues</code>.
525: */
526: protected void saveWidgetValues() {
527:
528: // allow subclasses to save values
529: internalSaveWidgetValues();
530:
531: }
532:
533: /**
534: * Set the initial selections in the resource group.
535: */
536: protected void setupBasedOnInitialSelections() {
537:
538: Iterator it = this .initialResourceSelection.iterator();
539: while (it.hasNext()) {
540: IResource currentResource = (IResource) it.next();
541: if (currentResource.getType() == IResource.FILE) {
542: this .resourceGroup
543: .initialCheckListItem(currentResource);
544: } else {
545: this .resourceGroup
546: .initialCheckTreeItem(currentResource);
547: }
548: }
549: }
550:
551: /**
552: * Update the tree to only select those elements that match the selected types
553: */
554: private void setupSelectionsBasedOnSelectedTypes() {
555:
556: Runnable runnable = new Runnable() {
557: public void run() {
558: Map selectionMap = new Hashtable();
559: //Only get the white selected ones
560: Iterator resourceIterator = resourceGroup
561: .getAllWhiteCheckedItems().iterator();
562: while (resourceIterator.hasNext()) {
563: //handle the files here - white checked containers require recursion
564: IResource resource = (IResource) resourceIterator
565: .next();
566: if (resource.getType() == IResource.FILE) {
567: if (hasExportableExtension(resource.getName())) {
568: List resourceList = new ArrayList();
569: IContainer parent = resource.getParent();
570: if (selectionMap.containsKey(parent)) {
571: resourceList = (List) selectionMap
572: .get(parent);
573: }
574: resourceList.add(resource);
575: selectionMap.put(parent, resourceList);
576: }
577: } else {
578: setupSelectionsBasedOnSelectedTypes(
579: selectionMap, (IContainer) resource);
580: }
581: }
582: resourceGroup.updateSelections(selectionMap);
583: }
584: };
585:
586: BusyIndicator.showWhile(getShell().getDisplay(), runnable);
587:
588: }
589:
590: /**
591: * Set up the selection values for the resources and put them in the selectionMap.
592: * If a resource is a file see if it matches one of the selected extensions. If not
593: * then check the children.
594: */
595: private void setupSelectionsBasedOnSelectedTypes(Map selectionMap,
596: IContainer parent) {
597:
598: List selections = new ArrayList();
599: IResource[] resources;
600: boolean hasFiles = false;
601:
602: try {
603: resources = parent.members();
604: } catch (CoreException exception) {
605: //Just return if we can't get any info
606: return;
607: }
608:
609: for (int i = 0; i < resources.length; i++) {
610: IResource resource = resources[i];
611: if (resource.getType() == IResource.FILE) {
612: if (hasExportableExtension(resource.getName())) {
613: hasFiles = true;
614: selections.add(resource);
615: }
616: } else {
617: setupSelectionsBasedOnSelectedTypes(selectionMap,
618: (IContainer) resource);
619: }
620: }
621:
622: //Only add it to the list if there are files in this folder
623: if (hasFiles) {
624: selectionMap.put(parent, selections);
625: }
626: }
627:
628: /**
629: * Save any editors that the user wants to save before export.
630: * @return boolean if the save was successful.
631: */
632: protected boolean saveDirtyEditors() {
633: return IDEWorkbenchPlugin.getDefault().getWorkbench()
634: .saveAllEditors(true);
635: }
636:
637: /**
638: * Check if widgets are enabled or disabled by a change in the dialog.
639: */
640: protected void updateWidgetEnablements() {
641:
642: boolean pageComplete = determinePageCompletion();
643: setPageComplete(pageComplete);
644: if (pageComplete) {
645: setMessage(null);
646: }
647: super.updateWidgetEnablements();
648: }
649: }
|