001: /*******************************************************************************
002: * Copyright (c) 2000, 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.dialogs;
011:
012: import java.util.Arrays;
013: import java.util.List;
014:
015: import org.eclipse.core.resources.IContainer;
016: import org.eclipse.core.runtime.IPath;
017: import org.eclipse.core.runtime.Path;
018: import org.eclipse.jface.dialogs.IDialogConstants;
019: import org.eclipse.jface.dialogs.MessageDialog;
020: import org.eclipse.jface.resource.JFaceResources;
021: import org.eclipse.jface.wizard.WizardPage;
022: import org.eclipse.osgi.util.NLS;
023: import org.eclipse.swt.SWT;
024: import org.eclipse.swt.graphics.Image;
025: import org.eclipse.swt.layout.GridData;
026: import org.eclipse.swt.layout.GridLayout;
027: import org.eclipse.swt.widgets.Composite;
028: import org.eclipse.swt.widgets.Group;
029: import org.eclipse.swt.widgets.Label;
030: import org.eclipse.swt.widgets.Listener;
031: import org.eclipse.swt.widgets.Text;
032: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
033:
034: /**
035: * The common superclass for wizard import and export pages.
036: * <p>
037: * This class is not intended to be subclassed outside of the workbench.
038: * </p>
039: */
040: public abstract class WizardDataTransferPage extends WizardPage
041: implements Listener, IOverwriteQuery {
042:
043: // constants
044: protected static final int SIZING_TEXT_FIELD_WIDTH = 250;
045:
046: protected static final int COMBO_HISTORY_LENGTH = 5;
047:
048: /**
049: * Creates a new wizard page.
050: *
051: * @param pageName the name of the page
052: */
053: protected WizardDataTransferPage(String pageName) {
054: super (pageName);
055: }
056:
057: /**
058: * Adds an entry to a history, while taking care of duplicate history items
059: * and excessively long histories. The assumption is made that all histories
060: * should be of length <code>WizardDataTransferPage.COMBO_HISTORY_LENGTH</code>.
061: *
062: * @param history the current history
063: * @param newEntry the entry to add to the history
064: */
065: protected String[] addToHistory(String[] history, String newEntry) {
066: java.util.ArrayList l = new java.util.ArrayList(Arrays
067: .asList(history));
068: addToHistory(l, newEntry);
069: String[] r = new String[l.size()];
070: l.toArray(r);
071: return r;
072: }
073:
074: /**
075: * Adds an entry to a history, while taking care of duplicate history items
076: * and excessively long histories. The assumption is made that all histories
077: * should be of length <code>WizardDataTransferPage.COMBO_HISTORY_LENGTH</code>.
078: *
079: * @param history the current history
080: * @param newEntry the entry to add to the history
081: */
082: protected void addToHistory(List history, String newEntry) {
083: history.remove(newEntry);
084: history.add(0, newEntry);
085:
086: // since only one new item was added, we can be over the limit
087: // by at most one item
088: if (history.size() > COMBO_HISTORY_LENGTH) {
089: history.remove(COMBO_HISTORY_LENGTH);
090: }
091: }
092:
093: /**
094: * Return whether the user is allowed to enter a new container name or just
095: * choose from existing ones.
096: * <p>
097: * Subclasses must implement this method.
098: * </p>
099: *
100: * @return <code>true</code> if new ones are okay, and <code>false</code>
101: * if only existing ones are allowed
102: */
103: protected abstract boolean allowNewContainerName();
104:
105: /**
106: * Creates a new label with a bold font.
107: *
108: * @param parent the parent control
109: * @param text the label text
110: * @return the new label control
111: */
112: protected Label createBoldLabel(Composite parent, String text) {
113: Label label = new Label(parent, SWT.NONE);
114: label.setFont(JFaceResources.getBannerFont());
115: label.setText(text);
116: GridData data = new GridData();
117: data.verticalAlignment = GridData.FILL;
118: data.horizontalAlignment = GridData.FILL;
119: label.setLayoutData(data);
120: return label;
121: }
122:
123: /**
124: * Creates the import/export options group controls.
125: * <p>
126: * The <code>WizardDataTransferPage</code> implementation of this method does
127: * nothing. Subclasses wishing to define such components should reimplement
128: * this hook method.
129: * </p>
130: *
131: * @param optionsGroup the parent control
132: */
133: protected void createOptionsGroupButtons(Group optionsGroup) {
134: }
135:
136: /**
137: * Creates a new label with a bold font.
138: *
139: * @param parent the parent control
140: * @param text the label text
141: * @return the new label control
142: */
143: protected Label createPlainLabel(Composite parent, String text) {
144: Label label = new Label(parent, SWT.NONE);
145: label.setText(text);
146: label.setFont(parent.getFont());
147: GridData data = new GridData();
148: data.verticalAlignment = GridData.FILL;
149: data.horizontalAlignment = GridData.FILL;
150: label.setLayoutData(data);
151: return label;
152: }
153:
154: /**
155: * Creates a horizontal spacer line that fills the width of its container.
156: *
157: * @param parent the parent control
158: */
159: protected void createSpacer(Composite parent) {
160: Label spacer = new Label(parent, SWT.NONE);
161: GridData data = new GridData();
162: data.horizontalAlignment = GridData.FILL;
163: data.verticalAlignment = GridData.BEGINNING;
164: spacer.setLayoutData(data);
165: }
166:
167: /**
168: * Returns whether this page is complete. This determination is made based upon
169: * the current contents of this page's controls. Subclasses wishing to include
170: * their controls in this determination should override the hook methods
171: * <code>validateSourceGroup</code> and/or <code>validateOptionsGroup</code>.
172: *
173: * @return <code>true</code> if this page is complete, and <code>false</code> if
174: * incomplete
175: * @see #validateSourceGroup
176: * @see #validateOptionsGroup
177: */
178: protected boolean determinePageCompletion() {
179: boolean complete = validateSourceGroup()
180: && validateDestinationGroup() && validateOptionsGroup();
181:
182: // Avoid draw flicker by not clearing the error
183: // message unless all is valid.
184: if (complete) {
185: setErrorMessage(null);
186: }
187:
188: return complete;
189: }
190:
191: /**
192: * Get a path from the supplied text widget.
193: * @return org.eclipse.core.runtime.IPath
194: */
195: protected IPath getPathFromText(Text textField) {
196: String text = textField.getText();
197: //Do not make an empty path absolute so as not to confuse with the root
198: if (text.length() == 0) {
199: return new Path(text);
200: }
201:
202: return (new Path(text)).makeAbsolute();
203: }
204:
205: /**
206: * Queries the user to supply a container resource.
207: *
208: * @return the path to an existing or new container, or <code>null</code> if the
209: * user cancelled the dialog
210: */
211: protected IPath queryForContainer(IContainer initialSelection,
212: String msg) {
213: return queryForContainer(initialSelection, msg, null);
214: }
215:
216: /**
217: * Queries the user to supply a container resource.
218: *
219: * @return the path to an existing or new container, or <code>null</code> if the
220: * user cancelled the dialog
221: */
222: protected IPath queryForContainer(IContainer initialSelection,
223: String msg, String title) {
224: ContainerSelectionDialog dialog = new ContainerSelectionDialog(
225: getControl().getShell(), initialSelection,
226: allowNewContainerName(), msg);
227: if (title != null) {
228: dialog.setTitle(title);
229: }
230: dialog.showClosedProjects(false);
231: dialog.open();
232: Object[] result = dialog.getResult();
233: if (result != null && result.length == 1) {
234: return (IPath) result[0];
235: }
236: return null;
237: }
238:
239: /**
240: * The <code>WizardDataTransfer</code> implementation of this
241: * <code>IOverwriteQuery</code> method asks the user whether the existing
242: * resource at the given path should be overwritten.
243: *
244: * @param pathString
245: * @return the user's reply: one of <code>"YES"</code>, <code>"NO"</code>, <code>"ALL"</code>,
246: * or <code>"CANCEL"</code>
247: */
248: public String queryOverwrite(String pathString) {
249:
250: Path path = new Path(pathString);
251:
252: String messageString;
253: //Break the message up if there is a file name and a directory
254: //and there are at least 2 segments.
255: if (path.getFileExtension() == null || path.segmentCount() < 2) {
256: messageString = NLS
257: .bind(
258: IDEWorkbenchMessages.WizardDataTransfer_existsQuestion,
259: pathString);
260: } else {
261: messageString = NLS
262: .bind(
263: IDEWorkbenchMessages.WizardDataTransfer_overwriteNameAndPathQuestion,
264: path.lastSegment(), path
265: .removeLastSegments(1).toOSString());
266: }
267:
268: final MessageDialog dialog = new MessageDialog(getContainer()
269: .getShell(), IDEWorkbenchMessages.Question, null,
270: messageString, MessageDialog.QUESTION, new String[] {
271: IDialogConstants.YES_LABEL,
272: IDialogConstants.YES_TO_ALL_LABEL,
273: IDialogConstants.NO_LABEL,
274: IDialogConstants.NO_TO_ALL_LABEL,
275: IDialogConstants.CANCEL_LABEL }, 0);
276: String[] response = new String[] { YES, ALL, NO, NO_ALL, CANCEL };
277: //run in syncExec because callback is from an operation,
278: //which is probably not running in the UI thread.
279: getControl().getDisplay().syncExec(new Runnable() {
280: public void run() {
281: dialog.open();
282: }
283: });
284: return dialog.getReturnCode() < 0 ? CANCEL : response[dialog
285: .getReturnCode()];
286: }
287:
288: /**
289: * Displays a Yes/No question to the user with the specified message and returns
290: * the user's response.
291: *
292: * @param message the question to ask
293: * @return <code>true</code> for Yes, and <code>false</code> for No
294: */
295: protected boolean queryYesNoQuestion(String message) {
296: MessageDialog dialog = new MessageDialog(getContainer()
297: .getShell(), IDEWorkbenchMessages.Question,
298: (Image) null, message, MessageDialog.NONE,
299: new String[] { IDialogConstants.YES_LABEL,
300: IDialogConstants.NO_LABEL }, 0);
301: // ensure yes is the default
302:
303: return dialog.open() == 0;
304: }
305:
306: /**
307: * Restores control settings that were saved in the previous instance of this
308: * page.
309: * <p>
310: * The <code>WizardDataTransferPage</code> implementation of this method does
311: * nothing. Subclasses may override this hook method.
312: * </p>
313: */
314: protected void restoreWidgetValues() {
315: }
316:
317: /**
318: * Saves control settings that are to be restored in the next instance of
319: * this page.
320: * <p>
321: * The <code>WizardDataTransferPage</code> implementation of this method does
322: * nothing. Subclasses may override this hook method.
323: * </p>
324: */
325: protected void saveWidgetValues() {
326: }
327:
328: /**
329: * Determine if the page is complete and update the page appropriately.
330: */
331: protected void updatePageCompletion() {
332: boolean pageComplete = determinePageCompletion();
333: setPageComplete(pageComplete);
334: if (pageComplete) {
335: setErrorMessage(null);
336: }
337: }
338:
339: /**
340: * Updates the enable state of this page's controls.
341: * <p>
342: * The <code>WizardDataTransferPage</code> implementation of this method does
343: * nothing. Subclasses may extend this hook method.
344: * </p>
345: */
346: protected void updateWidgetEnablements() {
347: }
348:
349: /**
350: * Returns whether this page's destination specification controls currently all
351: * contain valid values.
352: * <p>
353: * The <code>WizardDataTransferPage</code> implementation of this method returns
354: * <code>true</code>. Subclasses may reimplement this hook method.
355: * </p>
356: *
357: * @return <code>true</code> indicating validity of all controls in the
358: * destination specification group
359: */
360: protected boolean validateDestinationGroup() {
361: return true;
362: }
363:
364: /**
365: * Returns whether this page's options group's controls currently all contain
366: * valid values.
367: * <p>
368: * The <code>WizardDataTransferPage</code> implementation of this method returns
369: * <code>true</code>. Subclasses may reimplement this hook method.
370: * </p>
371: *
372: * @return <code>true</code> indicating validity of all controls in the options
373: * group
374: */
375: protected boolean validateOptionsGroup() {
376: return true;
377: }
378:
379: /**
380: * Returns whether this page's source specification controls currently all
381: * contain valid values.
382: * <p>
383: * The <code>WizardDataTransferPage</code> implementation of this method returns
384: * <code>true</code>. Subclasses may reimplement this hook method.
385: * </p>
386: *
387: * @return <code>true</code> indicating validity of all controls in the
388: * source specification group
389: */
390: protected boolean validateSourceGroup() {
391: return true;
392: }
393:
394: /**
395: * Create the options specification widgets.
396: *
397: * @param parent org.eclipse.swt.widgets.Composite
398: */
399: protected void createOptionsGroup(Composite parent) {
400: // options group
401: Group optionsGroup = new Group(parent, SWT.NONE);
402: GridLayout layout = new GridLayout();
403: optionsGroup.setLayout(layout);
404: optionsGroup.setLayoutData(new GridData(
405: GridData.HORIZONTAL_ALIGN_FILL
406: | GridData.GRAB_HORIZONTAL));
407: optionsGroup
408: .setText(IDEWorkbenchMessages.WizardExportPage_options);
409: optionsGroup.setFont(parent.getFont());
410:
411: createOptionsGroupButtons(optionsGroup);
412:
413: }
414:
415: /**
416: * Display an error dialog with the specified message.
417: *
418: * @param message the error message
419: */
420: protected void displayErrorDialog(String message) {
421: MessageDialog.openError(getContainer().getShell(),
422: getErrorDialogTitle(), message);
423: }
424:
425: /**
426: * Display an error dislog with the information from the
427: * supplied exception.
428: * @param exception Throwable
429: */
430: protected void displayErrorDialog(Throwable exception) {
431: String message = exception.getMessage();
432: //Some system exceptions have no message
433: if (message == null) {
434: message = NLS
435: .bind(
436: IDEWorkbenchMessages.WizardDataTransfer_exceptionMessage,
437: exception);
438: }
439: displayErrorDialog(message);
440: }
441:
442: /**
443: * Get the title for an error dialog. Subclasses should
444: * override.
445: */
446: protected String getErrorDialogTitle() {
447: return IDEWorkbenchMessages.WizardExportPage_internalErrorTitle;
448: }
449:
450: }
|