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: * Martin Boyle <martingboyle@gmail.com> - Fix for
011: * Bug 183013 [Wizards] Error importing into linked EFS folder - "undefined path variable"
012: *******************************************************************************/package org.eclipse.ui.dialogs;
013:
014: import java.util.ArrayList;
015: import java.util.Map;
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.resources.IWorkspace;
021: import org.eclipse.core.resources.IWorkspaceRoot;
022: import org.eclipse.core.runtime.IAdaptable;
023: import org.eclipse.core.runtime.IPath;
024: import org.eclipse.core.runtime.IProgressMonitor;
025: import org.eclipse.core.runtime.IStatus;
026: import org.eclipse.jface.viewers.CheckStateChangedEvent;
027: import org.eclipse.jface.viewers.ICheckStateListener;
028: import org.eclipse.jface.viewers.IStructuredSelection;
029: import org.eclipse.jface.viewers.ITreeContentProvider;
030: import org.eclipse.swt.SWT;
031: import org.eclipse.swt.custom.BusyIndicator;
032: import org.eclipse.swt.layout.GridData;
033: import org.eclipse.swt.layout.GridLayout;
034: import org.eclipse.swt.widgets.Button;
035: import org.eclipse.swt.widgets.Composite;
036: import org.eclipse.swt.widgets.Event;
037: import org.eclipse.swt.widgets.Label;
038: import org.eclipse.swt.widgets.Text;
039: import org.eclipse.swt.widgets.Widget;
040: import org.eclipse.ui.internal.ide.DialogUtil;
041: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
042: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
043: import org.eclipse.ui.internal.ide.dialogs.IElementFilter;
044: import org.eclipse.ui.internal.ide.dialogs.ResourceTreeAndListGroup;
045: import org.eclipse.ui.model.WorkbenchLabelProvider;
046: import org.eclipse.ui.model.WorkbenchViewerComparator;
047:
048: /**
049: * The abstract superclass for a typical import 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>createSourceGroup</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: * </ul>
071: * </p>
072: */
073: public abstract class WizardResourceImportPage extends
074: WizardDataTransferPage {
075: private IResource currentResourceSelection;
076:
077: // initial value stores
078: private String initialContainerFieldValue;
079:
080: protected java.util.List selectedTypes = new ArrayList();
081:
082: // widgets
083: private Text containerNameField;
084:
085: private Button containerBrowseButton;
086:
087: /**
088: * The <code>selectionGroup</code> field should have been created with a
089: * private modifier. Subclasses should not access this field directly.
090: */
091: protected ResourceTreeAndListGroup selectionGroup;
092:
093: //messages
094: private static final String EMPTY_FOLDER_MESSAGE = IDEWorkbenchMessages.WizardImportPage_specifyFolder;
095:
096: private static final String EMPTY_PROJECT_MESSAGE = IDEWorkbenchMessages.WizardImportPage_specifyProject;
097:
098: private static final String INACCESSABLE_FOLDER_MESSAGE = IDEWorkbenchMessages.WizardImportPage_folderMustExist;
099:
100: /**
101: * Creates an import wizard page. If the initial resource selection
102: * contains exactly one container resource then it will be used as the default
103: * import destination.
104: *
105: * @param name the name of the page
106: * @param selection the current resource selection
107: */
108: protected WizardResourceImportPage(String name,
109: IStructuredSelection selection) {
110: super (name);
111:
112: //Initialize to null
113: currentResourceSelection = null;
114: if (selection.size() == 1) {
115: Object firstElement = selection.getFirstElement();
116: if (firstElement instanceof IAdaptable) {
117: Object resource = ((IAdaptable) firstElement)
118: .getAdapter(IResource.class);
119: if (resource != null) {
120: currentResourceSelection = (IResource) resource;
121: }
122: }
123: }
124:
125: if (currentResourceSelection != null) {
126: if (currentResourceSelection.getType() == IResource.FILE) {
127: currentResourceSelection = currentResourceSelection
128: .getParent();
129: }
130:
131: if (!currentResourceSelection.isAccessible()) {
132: currentResourceSelection = null;
133: }
134: }
135:
136: }
137:
138: /**
139: * The <code>WizardResourceImportPage</code> implementation of this
140: * <code>WizardDataTransferPage</code> method returns <code>true</code>.
141: * Subclasses may override this method.
142: */
143: protected boolean allowNewContainerName() {
144: return true;
145: }
146:
147: /** (non-Javadoc)
148: * Method declared on IDialogPage.
149: */
150: public void createControl(Composite parent) {
151:
152: initializeDialogUnits(parent);
153:
154: Composite composite = new Composite(parent, SWT.NULL);
155: composite.setLayout(new GridLayout());
156: composite.setLayoutData(new GridData(
157: GridData.VERTICAL_ALIGN_FILL
158: | GridData.HORIZONTAL_ALIGN_FILL));
159: composite.setSize(composite.computeSize(SWT.DEFAULT,
160: SWT.DEFAULT));
161: composite.setFont(parent.getFont());
162:
163: createSourceGroup(composite);
164:
165: createDestinationGroup(composite);
166:
167: createOptionsGroup(composite);
168:
169: restoreWidgetValues();
170: updateWidgetEnablements();
171: setPageComplete(determinePageCompletion());
172: setErrorMessage(null); // should not initially have error message
173:
174: setControl(composite);
175: }
176:
177: /**
178: * Creates the import destination specification controls.
179: *
180: * @param parent the parent control
181: */
182: protected final void createDestinationGroup(Composite parent) {
183: // container specification group
184: Composite containerGroup = new Composite(parent, SWT.NONE);
185: GridLayout layout = new GridLayout();
186: layout.numColumns = 3;
187: containerGroup.setLayout(layout);
188: containerGroup.setLayoutData(new GridData(
189: GridData.HORIZONTAL_ALIGN_FILL
190: | GridData.GRAB_HORIZONTAL));
191: containerGroup.setFont(parent.getFont());
192:
193: // container label
194: Label resourcesLabel = new Label(containerGroup, SWT.NONE);
195: resourcesLabel
196: .setText(IDEWorkbenchMessages.WizardImportPage_folder);
197: resourcesLabel.setFont(parent.getFont());
198:
199: // container name entry field
200: containerNameField = new Text(containerGroup, SWT.SINGLE
201: | SWT.BORDER);
202: containerNameField.addListener(SWT.Modify, this );
203: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
204: | GridData.GRAB_HORIZONTAL);
205: data.widthHint = SIZING_TEXT_FIELD_WIDTH;
206: containerNameField.setLayoutData(data);
207: containerNameField.setFont(parent.getFont());
208:
209: // container browse button
210: containerBrowseButton = new Button(containerGroup, SWT.PUSH);
211: containerBrowseButton
212: .setText(IDEWorkbenchMessages.WizardImportPage_browse2);
213: containerBrowseButton.setLayoutData(new GridData(
214: GridData.HORIZONTAL_ALIGN_FILL));
215: containerBrowseButton.addListener(SWT.Selection, this );
216: containerBrowseButton.setFont(parent.getFont());
217: setButtonLayoutData(containerBrowseButton);
218:
219: initialPopulateContainerField();
220: }
221:
222: /**
223: * Create the import source selection widget
224: */
225: protected void createFileSelectionGroup(Composite parent) {
226:
227: //Just create with a dummy root.
228: this .selectionGroup = new ResourceTreeAndListGroup(
229: parent,
230: new FileSystemElement("Dummy", null, true),//$NON-NLS-1$
231: getFolderProvider(), new WorkbenchLabelProvider(),
232: getFileProvider(), new WorkbenchLabelProvider(),
233: SWT.NONE, DialogUtil.inRegularFontMode(parent));
234:
235: ICheckStateListener listener = new ICheckStateListener() {
236: public void checkStateChanged(CheckStateChangedEvent event) {
237: updateWidgetEnablements();
238: }
239: };
240:
241: WorkbenchViewerComparator comparator = new WorkbenchViewerComparator();
242: this .selectionGroup.setTreeComparator(comparator);
243: this .selectionGroup.setListComparator(comparator);
244: this .selectionGroup.addCheckStateListener(listener);
245:
246: }
247:
248: /**
249: * Creates the import source specification controls.
250: * <p>
251: * Subclasses must implement this method.
252: * </p>
253: *
254: * @param parent the parent control
255: */
256: protected abstract void createSourceGroup(Composite parent);
257:
258: /*
259: * @see WizardDataTransferPage.getErrorDialogTitle()
260: */
261: protected String getErrorDialogTitle() {
262: return IDEWorkbenchMessages.WizardImportPage_errorDialogTitle;
263: }
264:
265: /**
266: * Returns the path of the container resource specified in the container
267: * name entry field, or <code>null</code> if no name has been typed in.
268: * <p>
269: * The container specified by the full path might not exist and would need to
270: * be created.
271: * </p>
272: *
273: * @return the full path of the container resource specified in
274: * the container name entry field, or <code>null</code>
275: */
276: protected IPath getContainerFullPath() {
277: IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
278:
279: //make the path absolute to allow for optional leading slash
280: IPath testPath = getResourcePath();
281:
282: if (testPath.equals(workspace.getRoot().getFullPath())) {
283: return testPath;
284: }
285:
286: IStatus result = workspace.validatePath(testPath.toString(),
287: IResource.PROJECT | IResource.FOLDER | IResource.ROOT);
288: if (result.isOK()) {
289: return testPath;
290: }
291:
292: return null;
293: }
294:
295: /**
296: * Returns a content provider for <code>FileSystemElement</code>s that returns
297: * only files as children.
298: */
299: protected abstract ITreeContentProvider getFileProvider();
300:
301: /**
302: * Returns a content provider for <code>FileSystemElement</code>s that returns
303: * only folders as children.
304: */
305: protected abstract ITreeContentProvider getFolderProvider();
306:
307: /**
308: * Return the path for the resource field.
309: * @return IPath
310: */
311: protected IPath getResourcePath() {
312: return getPathFromText(this .containerNameField);
313: }
314:
315: /**
316: * Returns this page's list of currently-specified resources to be
317: * imported. This is the primary resource selection facility accessor for
318: * subclasses.
319: *
320: * @return a list of resources currently selected
321: * for export (element type: <code>IResource</code>)
322: */
323: protected java.util.List getSelectedResources() {
324: return this .selectionGroup.getAllCheckedListItems();
325: }
326:
327: /**
328: * Returns this page's list of currently-specified resources to be
329: * imported filtered by the IElementFilter.
330: *
331: */
332: protected void getSelectedResources(IElementFilter filter,
333: IProgressMonitor monitor) throws InterruptedException {
334: this .selectionGroup.getAllCheckedListItems(filter, monitor);
335: }
336:
337: /**
338: * Returns the container resource specified in the container name entry field,
339: * or <code>null</code> if such a container does not exist in the workbench.
340: *
341: * @return the container resource specified in the container name entry field,
342: * or <code>null</code>
343: */
344: protected IContainer getSpecifiedContainer() {
345: IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
346: IPath path = getContainerFullPath();
347: if (workspace.getRoot().exists(path)) {
348: IResource resource = workspace.getRoot().findMember(path);
349: if (resource.getType() == IResource.FILE) {
350: return null;
351: }
352: return (IContainer) resource;
353:
354: }
355:
356: return null;
357: }
358:
359: /**
360: * Returns a collection of the currently-specified resource types for
361: * use by the type selection dialog.
362: */
363: protected java.util.List getTypesToImport() {
364:
365: return selectedTypes;
366: }
367:
368: /**
369: * Opens a container selection dialog and displays the user's subsequent
370: * container resource selection in this page's container name field.
371: */
372: protected void handleContainerBrowseButtonPressed() {
373: // see if the user wishes to modify this container selection
374: IPath containerPath = queryForContainer(
375: getSpecifiedContainer(),
376: IDEWorkbenchMessages.WizardImportPage_selectFolderLabel,
377: IDEWorkbenchMessages.WizardImportPage_selectFolderTitle);
378:
379: // if a container was selected then put its name in the container name field
380: if (containerPath != null) { // null means user cancelled
381: setErrorMessage(null);
382: containerNameField.setText(containerPath.makeRelative()
383: .toString());
384: }
385: }
386:
387: /**
388: * The <code>WizardResourceImportPage</code> implementation of this
389: * <code>Listener</code> method handles all events and enablements for controls
390: * on this page. Subclasses may extend.
391: * @param event Event
392: */
393: public void handleEvent(Event event) {
394: Widget source = event.widget;
395:
396: if (source == containerBrowseButton) {
397: handleContainerBrowseButtonPressed();
398: }
399:
400: updateWidgetEnablements();
401: }
402:
403: /**
404: * Open a registered type selection dialog and note the selections
405: * in the receivers types-to-export field
406: */
407: protected void handleTypesEditButtonPressed() {
408:
409: TypeFilteringDialog dialog = new TypeFilteringDialog(
410: getContainer().getShell(), getTypesToImport());
411:
412: dialog.open();
413:
414: Object[] newSelectedTypes = dialog.getResult();
415: if (newSelectedTypes != null) { // ie.- did not press Cancel
416: this .selectedTypes = new ArrayList(newSelectedTypes.length);
417: for (int i = 0; i < newSelectedTypes.length; i++) {
418: this .selectedTypes.add(newSelectedTypes[i]);
419: }
420:
421: setupSelectionsBasedOnSelectedTypes();
422: }
423:
424: }
425:
426: /**
427: * Sets the initial contents of the container name field.
428: */
429: protected final void initialPopulateContainerField() {
430: if (initialContainerFieldValue != null) {
431: containerNameField.setText(initialContainerFieldValue);
432: } else if (currentResourceSelection != null) {
433: containerNameField.setText(currentResourceSelection
434: .getFullPath().makeRelative().toString());
435: }
436: }
437:
438: /**
439: * Set all of the selections in the selection group to value
440: * @param value boolean
441: */
442: protected void setAllSelections(boolean value) {
443: selectionGroup.setAllSelections(value);
444: }
445:
446: /**
447: * Sets the value of this page's container resource field, or stores
448: * it for future use if this page's controls do not exist yet.
449: *
450: * @param value String
451: */
452: public void setContainerFieldValue(String value) {
453: if (containerNameField == null) {
454: initialContainerFieldValue = value;
455: } else {
456: containerNameField.setText(value);
457: }
458: }
459:
460: /**
461: * Update the tree to only select those elements that match the selected types.
462: * Do nothing by default.
463: */
464: protected void setupSelectionsBasedOnSelectedTypes() {
465: }
466:
467: /**
468: * Update the selections with those in map .
469: * @param map Map - key tree elements, values Lists of list elements
470: */
471: protected void updateSelections(final Map map) {
472:
473: Runnable runnable = new Runnable() {
474: public void run() {
475: selectionGroup.updateSelections(map);
476: }
477: };
478:
479: BusyIndicator.showWhile(getShell().getDisplay(), runnable);
480: }
481:
482: /**
483: * Check if widgets are enabled or disabled by a change in the dialog.
484: */
485: protected void updateWidgetEnablements() {
486:
487: boolean pageComplete = determinePageCompletion();
488: setPageComplete(pageComplete);
489: if (pageComplete) {
490: setMessage(null);
491: }
492: super .updateWidgetEnablements();
493: }
494:
495: /* (non-Javadoc)
496: * Method declared on WizardDataTransferPage.
497: */
498: protected final boolean validateDestinationGroup() {
499:
500: IPath containerPath = getContainerFullPath();
501: if (containerPath == null) {
502: setMessage(EMPTY_FOLDER_MESSAGE);
503: return false;
504: }
505:
506: // If the container exist, validate it
507: IContainer container = getSpecifiedContainer();
508: if (container == null) {
509: //If it exists but is not valid then abort
510: if (IDEWorkbenchPlugin.getPluginWorkspace().getRoot()
511: .exists(getContainerFullPath())) {
512: return false;
513: }
514:
515: //if it is does not exist be sure the project does
516: IWorkspace workspace = IDEWorkbenchPlugin
517: .getPluginWorkspace();
518: IPath projectPath = containerPath
519: .removeLastSegments(containerPath.segmentCount() - 1);
520:
521: if (workspace.getRoot().exists(projectPath)) {
522: return true;
523: }
524: setErrorMessage(IDEWorkbenchMessages.WizardImportPage_projectNotExist);
525: return false;
526: }
527: if (!container.isAccessible()) {
528: setErrorMessage(INACCESSABLE_FOLDER_MESSAGE);
529: return false;
530: }
531: if (container.getLocationURI() == null) {
532: if (container.isLinked()) {
533: setErrorMessage(IDEWorkbenchMessages.WizardImportPage_undefinedPathVariable);
534: } else {
535: setErrorMessage(IDEWorkbenchMessages.WizardImportPage_containerNotExist);
536: }
537: return false;
538: }
539:
540: if (sourceConflictsWithDestination(containerPath)) {
541: setErrorMessage(getSourceConflictMessage());
542: return false;
543: }
544:
545: if (container instanceof IWorkspaceRoot) {
546: setErrorMessage(EMPTY_PROJECT_MESSAGE);
547: return false;
548: }
549: return true;
550:
551: }
552:
553: /**
554: * Returns the error message for when the source conflicts
555: * with the destination.
556: */
557: protected final String getSourceConflictMessage() {
558: return (IDEWorkbenchMessages.WizardImportPage_importOnReceiver);
559: }
560:
561: /**
562: * Returns whether or not the source location conflicts
563: * with the destination resource. By default this is not
564: * checked, so <code>false</code> is returned.
565: *
566: * @param sourcePath the path being checked
567: * @return <code>true</code> if the source location conflicts with the
568: * destination resource, <code>false</code> if not
569: */
570: protected boolean sourceConflictsWithDestination(IPath sourcePath) {
571: return false;
572: }
573:
574: /*
575: * @see WizardDataTransferPage.determinePageCompletion.
576: */
577: protected boolean determinePageCompletion() {
578: //Check for valid projects before making the user do anything
579: if (noOpenProjects()) {
580: setErrorMessage(IDEWorkbenchMessages.WizardImportPage_noOpenProjects);
581: return false;
582: }
583: return super .determinePageCompletion();
584: }
585:
586: /**
587: * Returns whether or not the passed workspace has any
588: * open projects
589: * @return boolean
590: */
591: private boolean noOpenProjects() {
592: IProject[] projects = IDEWorkbenchPlugin.getPluginWorkspace()
593: .getRoot().getProjects();
594: for (int i = 0; i < projects.length; i++) {
595: if (projects[i].isOpen()) {
596: return false;
597: }
598: }
599: return true;
600: }
601: }
|