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: * Sebastian Davids <sdavids@gmx.de> - 19346, 42056
011: *******************************************************************************/package org.eclipse.ui.internal.ide.dialogs;
012:
013: import java.util.Set;
014:
015: import org.eclipse.core.resources.IPathVariableManager;
016: import org.eclipse.core.resources.IResource;
017: import org.eclipse.core.runtime.IStatus;
018: import org.eclipse.core.runtime.Path;
019: import org.eclipse.jface.dialogs.Dialog;
020: import org.eclipse.jface.dialogs.IDialogConstants;
021: import org.eclipse.jface.dialogs.IMessageProvider;
022: import org.eclipse.jface.dialogs.TitleAreaDialog;
023: import org.eclipse.swt.SWT;
024: import org.eclipse.swt.events.ModifyEvent;
025: import org.eclipse.swt.events.ModifyListener;
026: import org.eclipse.swt.events.SelectionAdapter;
027: import org.eclipse.swt.events.SelectionEvent;
028: import org.eclipse.swt.layout.FormAttachment;
029: import org.eclipse.swt.layout.FormData;
030: import org.eclipse.swt.layout.FormLayout;
031: import org.eclipse.swt.layout.GridData;
032: import org.eclipse.swt.widgets.Button;
033: import org.eclipse.swt.widgets.Composite;
034: import org.eclipse.swt.widgets.Control;
035: import org.eclipse.swt.widgets.DirectoryDialog;
036: import org.eclipse.swt.widgets.FileDialog;
037: import org.eclipse.swt.widgets.Label;
038: import org.eclipse.swt.widgets.Shell;
039: import org.eclipse.swt.widgets.Text;
040: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
041:
042: /**
043: * Dialog that prompts the user for defining a variable's name and value. It
044: * supports creating a new variable or editing an existing one. The difference
045: * between the two uses is just a matter of which messages to present to the
046: * user and whether the "Ok" button starts enabled or not.
047: */
048: public class PathVariableDialog extends TitleAreaDialog {
049:
050: // UI widgets
051: private Button okButton;
052:
053: private Label variableNameLabel;
054:
055: private Label variableValueLabel;
056:
057: private Text variableNameField;
058:
059: private Text variableValueField;
060:
061: private Button fileButton;
062:
063: private Button folderButton;
064:
065: /**
066: * This dialog type: <code>NEW_VARIABLE</code> or
067: * <code>EXISTING_VARIABLE</code>.
068: */
069: private int type;
070:
071: /**
072: * The type of variable that can be edited in this dialog.
073: * <code>IResource.FILE</code> or <code>IResource.FOLDER</code>
074: */
075: private int variableType;
076:
077: /**
078: * The name of the variable being edited.
079: */
080: private String variableName;
081:
082: /**
083: * The value of the variable being edited.
084: */
085: private String variableValue;
086:
087: /**
088: * The original name of the variable being edited. It is used when testing
089: * if the current variable's name is already in use.
090: */
091: private String originalName;
092:
093: /**
094: * Used to select the proper message depending on the current mode
095: * (new/existing variable).
096: */
097: private boolean newVariable;
098:
099: /**
100: * Reference to the path variable manager. It is used for validating
101: * variable names.
102: */
103: private IPathVariableManager pathVariableManager;
104:
105: /**
106: * Set of variable names currently in use. Used when warning the user that
107: * the currently selected name is already in use by another variable.
108: */
109: private Set namesInUse;
110:
111: /**
112: * The current validation status. Its value can be one of the following:<ul>
113: * <li><code>IMessageProvider.NONE</code> (default);</li>
114: * <li><code>IMessageProvider.WARNING</code>;</li>
115: * <li><code>IMessageProvider.ERROR</code>;</li>
116: * </ul>
117: * Used when validating the user input.
118: */
119: private int validationStatus;
120:
121: /**
122: * The current validation message generated by the last
123: * call to a <code>validate</code> method.
124: */
125: private String validationMessage;
126:
127: /**
128: * Whether a variable name has been entered.
129: */
130: private boolean nameEntered = false;
131:
132: /**
133: * Whether a variable location has been entered.
134: */
135: private boolean locationEntered = false;
136:
137: /**
138: * The standard message to be shown when there are no problems being
139: * reported.
140: */
141: final private String standardMessage;
142:
143: /**
144: * Constant for defining this dialog as intended to create a new variable
145: * (value = 1).
146: */
147: public final static int NEW_VARIABLE = 1;
148:
149: /**
150: * Constant for defining this dialog as intended to edit an existing
151: * variable (value = 2).
152: */
153: public final static int EXISTING_VARIABLE = 2;
154:
155: /**
156: * Constructs a dialog for editing a new/existing path variable.
157: *
158: * @param parentShell the parent shell
159: * @param type the dialog type: <code>NEW_VARIABLE</code> or
160: * <code>EXISTING_VARIABLE</code>
161: * @param variableType the type of variable that can be edited in
162: * this dialog. <code>IResource.FILE</code> or <code>IResource.FOLDER</code>
163: * @param pathVariableManager a reference to the path variable manager
164: * @param namesInUse a set of variable names currently in use
165: */
166: public PathVariableDialog(Shell parentShell, int type,
167: int variableType, IPathVariableManager pathVariableManager,
168: Set namesInUse) {
169: super (parentShell);
170: this .type = type;
171: this .newVariable = type == NEW_VARIABLE;
172: this .variableName = ""; //$NON-NLS-1$
173: this .variableValue = ""; //$NON-NLS-1$
174: this .variableType = variableType;
175: this .pathVariableManager = pathVariableManager;
176: this .namesInUse = namesInUse;
177:
178: if (newVariable) {
179: this .standardMessage = IDEWorkbenchMessages.PathVariableDialog_message_newVariable;
180: } else {
181: this .standardMessage = IDEWorkbenchMessages.PathVariableDialog_message_existingVariable;
182: }
183: }
184:
185: /**
186: * Configures this dialog's shell, setting the shell's text.
187: *
188: * @see org.eclipse.jface.window.Window#configureShell(Shell)
189: */
190: protected void configureShell(Shell shell) {
191: super .configureShell(shell);
192: if (newVariable) {
193: shell
194: .setText(IDEWorkbenchMessages.PathVariableDialog_shellTitle_newVariable);
195: } else {
196: shell
197: .setText(IDEWorkbenchMessages.PathVariableDialog_shellTitle_existingVariable);
198: }
199: }
200:
201: /**
202: * Creates and returns the contents of this dialog (except for the button bar).
203: *
204: * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea
205: */
206: protected Control createDialogArea(Composite parent) {
207: // top level composite
208: Composite parentComposite = (Composite) super
209: .createDialogArea(parent);
210:
211: initializeDialogUnits(parentComposite);
212:
213: // creates dialog area composite
214: Composite contents = createComposite(parentComposite);
215:
216: // creates and lay outs dialog area widgets
217: createWidgets(contents);
218:
219: // validate possibly already incorrect variable definitions
220: if (type == EXISTING_VARIABLE) {
221: nameEntered = locationEntered = true;
222: validateVariableValue();
223: }
224:
225: Dialog.applyDialogFont(parentComposite);
226:
227: return contents;
228: }
229:
230: /**
231: * Creates and configures this dialog's main composite.
232: *
233: * @param parentComposite parent's composite
234: * @return this dialog's main composite
235: */
236: private Composite createComposite(Composite parentComposite) {
237: // creates a composite with standard margins and spacing
238: Composite contents = new Composite(parentComposite, SWT.NONE);
239:
240: FormLayout layout = new FormLayout();
241:
242: contents.setLayout(layout);
243: contents.setLayoutData(new GridData(GridData.FILL_BOTH));
244:
245: if (newVariable) {
246: setTitle(IDEWorkbenchMessages.PathVariableDialog_dialogTitle_newVariable);
247: } else {
248: setTitle(IDEWorkbenchMessages.PathVariableDialog_dialogTitle_existingVariable);
249: }
250: setMessage(standardMessage);
251: return contents;
252: }
253:
254: /**
255: * Creates widgets for this dialog.
256: *
257: * @param contents the parent composite where to create widgets
258: */
259: private void createWidgets(Composite contents) {
260: FormData data;
261:
262: String nameLabelText = IDEWorkbenchMessages.PathVariableDialog_variableName;
263: String valueLabelText = IDEWorkbenchMessages.PathVariableDialog_variableValue;
264:
265: // variable name label
266: variableNameLabel = new Label(contents, SWT.LEFT);
267: variableNameLabel.setText(nameLabelText);
268:
269: data = new FormData();
270: data.top = new FormAttachment(
271: 0,
272: convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN));
273: data.left = new FormAttachment(
274: 0,
275: convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN));
276: variableNameLabel.setLayoutData(data);
277:
278: // variable name field. Attachments done after all widgets created.
279: variableNameField = new Text(contents, SWT.SINGLE | SWT.BORDER);
280: variableNameField.setText(variableName);
281: variableNameField.addModifyListener(new ModifyListener() {
282: public void modifyText(ModifyEvent event) {
283: variableNameModified();
284: }
285: });
286:
287: // variable value label
288: variableValueLabel = new Label(contents, SWT.LEFT);
289: variableValueLabel.setText(valueLabelText);
290:
291: data = new FormData();
292: data.left = new FormAttachment(
293: 0,
294: convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN));
295: data.top = new FormAttachment(variableNameLabel,
296: convertVerticalDLUsToPixels(5));
297: variableValueLabel.setLayoutData(data);
298:
299: // variable value field. Attachments done after all widgets created.
300: variableValueField = new Text(contents, SWT.SINGLE | SWT.BORDER);
301: variableValueField.setText(variableValue);
302: variableValueField.addModifyListener(new ModifyListener() {
303: public void modifyText(ModifyEvent event) {
304: variableValueModified();
305: }
306: });
307:
308: // select file path button
309: fileButton = new Button(contents, SWT.PUSH);
310: fileButton
311: .setText(IDEWorkbenchMessages.PathVariableDialog_file);
312: if ((variableType & IResource.FILE) == 0) {
313: fileButton.setEnabled(false);
314: }
315:
316: data = setButtonFormLayoutData(fileButton);
317: data.top = new FormAttachment(variableValueLabel, 0, SWT.CENTER);
318: data.right = new FormAttachment(
319: 100,
320: -convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN));
321: fileButton.setLayoutData(data);
322:
323: fileButton.addSelectionListener(new SelectionAdapter() {
324: public void widgetSelected(SelectionEvent e) {
325: selectFile();
326: }
327: });
328:
329: // select folder path button
330: folderButton = new Button(contents, SWT.PUSH);
331: folderButton
332: .setText(IDEWorkbenchMessages.PathVariableDialog_folder);
333: if ((variableType & IResource.FOLDER) == 0) {
334: folderButton.setEnabled(false);
335: }
336:
337: data = setButtonFormLayoutData(folderButton);
338: data.top = new FormAttachment(fileButton,
339: convertVerticalDLUsToPixels(2));
340: data.right = new FormAttachment(
341: 100,
342: -convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN));
343: folderButton.setLayoutData(data);
344:
345: folderButton.addSelectionListener(new SelectionAdapter() {
346: public void widgetSelected(SelectionEvent e) {
347: selectFolder();
348: }
349: });
350:
351: // Attaching variable name and value fields to file and folder buttons,
352: // so do this now that those buttons have been created.
353:
354: // the larger label will be used in the left attachments for the fields
355: Label largerLabel = nameLabelText.length() > valueLabelText
356: .length() ? variableNameLabel : variableValueLabel;
357:
358: data = new FormData();
359: data.left = new FormAttachment(largerLabel,
360: convertHorizontalDLUsToPixels(5));
361: data.right = new FormAttachment(fileButton,
362: -convertHorizontalDLUsToPixels(5));
363: data.top = new FormAttachment(variableNameLabel,
364: convertVerticalDLUsToPixels(5), SWT.CENTER);
365: variableNameField.setLayoutData(data);
366:
367: data = new FormData();
368: data.left = new FormAttachment(largerLabel,
369: convertHorizontalDLUsToPixels(5));
370: data.right = new FormAttachment(fileButton,
371: -convertHorizontalDLUsToPixels(5));
372: data.top = new FormAttachment(variableValueLabel, 0, SWT.CENTER);
373: variableValueField.setLayoutData(data);
374:
375: }
376:
377: /**
378: * Sets the <code>FormData</code> on the specified button to be one that is
379: * spaced for the current dialog page units. The method
380: * <code>initializeDialogUnits</code> must be called once before calling this
381: * method for the first time.
382: *
383: * @param button the button to set the <code>FormData</code>
384: * @return the <code>FormData</code> set on the specified button
385: */
386: private FormData setButtonFormLayoutData(Button button) {
387: FormData data = new FormData();
388: int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
389: data.width = Math.max(widthHint, button.computeSize(
390: SWT.DEFAULT, SWT.DEFAULT, true).x);
391: button.setLayoutData(data);
392: return data;
393: }
394:
395: /**
396: * Fires validations (variable name first) and updates enabled state for the
397: * "Ok" button accordingly.
398: */
399: private void variableNameModified() {
400: // updates and validates the variable name
401: variableName = variableNameField.getText();
402: validationStatus = IMessageProvider.NONE;
403: okButton.setEnabled(validateVariableName()
404: && validateVariableValue());
405: nameEntered = true;
406: }
407:
408: /**
409: * Fires validations (variable value first) and updates enabled state for the
410: * "Ok" button accordingly.
411: */
412: private void variableValueModified() {
413: // updates and validates the variable value
414: variableValue = variableValueField.getText().trim();
415: validationStatus = IMessageProvider.NONE;
416: okButton.setEnabled(validateVariableValue()
417: && validateVariableName());
418: locationEntered = true;
419: }
420:
421: /**
422: * Opens a dialog where the user can select a folder path.
423: */
424: private void selectFolder() {
425: DirectoryDialog dialog = new DirectoryDialog(getShell());
426: dialog
427: .setText(IDEWorkbenchMessages.PathVariableDialog_selectFolderTitle);
428: dialog
429: .setMessage(IDEWorkbenchMessages.PathVariableDialog_selectFolderMessage);
430: dialog.setFilterPath(variableValue);
431: String res = dialog.open();
432: if (res != null) {
433: variableValue = new Path(res).makeAbsolute().toOSString();
434: variableValueField.setText(variableValue);
435: }
436: }
437:
438: /**
439: * Opens a dialog where the user can select a file path.
440: */
441: private void selectFile() {
442: FileDialog dialog = new FileDialog(getShell());
443: dialog
444: .setText(IDEWorkbenchMessages.PathVariableDialog_selectFileTitle);
445: dialog.setFilterPath(variableValue);
446: String res = dialog.open();
447: if (res != null) {
448: variableValue = new Path(res).makeAbsolute().toOSString();
449: variableValueField.setText(variableValue);
450: }
451: }
452:
453: /**
454: * Adds buttons to this dialog's button bar.
455: *
456: * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar
457: */
458: protected void createButtonsForButtonBar(Composite parent) {
459: okButton = createButton(parent, IDialogConstants.OK_ID,
460: IDialogConstants.OK_LABEL, true);
461: okButton.setEnabled(type == EXISTING_VARIABLE);
462:
463: createButton(parent, IDialogConstants.CANCEL_ID,
464: IDialogConstants.CANCEL_LABEL, false);
465: }
466:
467: /**
468: * Validates the current variable name, and updates this dialog's message.
469: *
470: * @return true if the name is valid, false otherwise
471: */
472: private boolean validateVariableName() {
473: boolean allowFinish = false;
474:
475: // if the current validationStatus is ERROR, no additional validation applies
476: if (validationStatus == IMessageProvider.ERROR) {
477: return false;
478: }
479:
480: // assumes everything will be ok
481: String message = standardMessage;
482: int newValidationStatus = IMessageProvider.NONE;
483:
484: if (variableName.length() == 0) {
485: // the variable name is empty
486: if (nameEntered) {
487: // a name was entered before and is now empty
488: newValidationStatus = IMessageProvider.ERROR;
489: message = IDEWorkbenchMessages.PathVariableDialog_variableNameEmptyMessage;
490: }
491: } else {
492: IStatus status = pathVariableManager
493: .validateName(variableName);
494: if (!status.isOK()) {
495: // the variable name is not valid
496: newValidationStatus = IMessageProvider.ERROR;
497: message = status.getMessage();
498: } else if (namesInUse.contains(variableName)
499: && !variableName.equals(originalName)) {
500: // the variable name is already in use
501: message = IDEWorkbenchMessages.PathVariableDialog_variableAlreadyExistsMessage;
502: newValidationStatus = IMessageProvider.ERROR;
503: } else {
504: allowFinish = true;
505: }
506: }
507:
508: // overwrite the current validation status / message only if everything is ok (clearing them)
509: // or if we have a more serious problem than the current one
510: if (validationStatus == IMessageProvider.NONE
511: || newValidationStatus == IMessageProvider.ERROR) {
512: validationStatus = newValidationStatus;
513: validationMessage = message;
514: }
515: // only set the message here if it is not going to be set in
516: // validateVariableValue to avoid flashing.
517: if (allowFinish == false) {
518: setMessage(validationMessage, validationStatus);
519: }
520: return allowFinish;
521: }
522:
523: /**
524: * Validates the current variable value, and updates this dialog's message.
525: *
526: * @return true if the value is valid, false otherwise
527: */
528: private boolean validateVariableValue() {
529: boolean allowFinish = false;
530:
531: // if the current validationStatus is ERROR, no additional validation applies
532: if (validationStatus == IMessageProvider.ERROR) {
533: return false;
534: }
535:
536: // assumes everything will be ok
537: String message = standardMessage;
538: int newValidationStatus = IMessageProvider.NONE;
539:
540: if (variableValue.length() == 0) {
541: // the variable value is empty
542: if (locationEntered) {
543: // a location value was entered before and is now empty
544: newValidationStatus = IMessageProvider.ERROR;
545: message = IDEWorkbenchMessages.PathVariableDialog_variableValueEmptyMessage;
546: }
547: } else if (!Path.EMPTY.isValidPath(variableValue)) {
548: // the variable value is an invalid path
549: message = IDEWorkbenchMessages.PathVariableDialog_variableValueInvalidMessage;
550: newValidationStatus = IMessageProvider.ERROR;
551: } else if (!new Path(variableValue).isAbsolute()) {
552: // the variable value is a relative path
553: message = IDEWorkbenchMessages.PathVariableDialog_pathIsRelativeMessage;
554: newValidationStatus = IMessageProvider.ERROR;
555: } else if (!IDEResourceInfoUtils.exists(variableValue)) {
556: // the path does not exist (warning)
557: message = IDEWorkbenchMessages.PathVariableDialog_pathDoesNotExistMessage;
558: newValidationStatus = IMessageProvider.WARNING;
559: allowFinish = true;
560: } else {
561: allowFinish = true;
562: }
563:
564: // overwrite the current validation status / message only if everything is ok (clearing them)
565: // or if we have a more serious problem than the current one
566: if (validationStatus == IMessageProvider.NONE
567: || newValidationStatus > validationStatus) {
568: validationStatus = newValidationStatus;
569: validationMessage = message;
570: }
571: setMessage(validationMessage, validationStatus);
572: return allowFinish;
573: }
574:
575: /**
576: * Returns the variable name.
577: *
578: * @return the variable name
579: */
580: public String getVariableName() {
581: return variableName;
582: }
583:
584: /**
585: * Returns the variable value.
586: *
587: * @return the variable value
588: */
589: public String getVariableValue() {
590: return variableValue;
591: }
592:
593: /**
594: * Sets the variable name.
595: *
596: * @param variableName the new variable name
597: */
598: public void setVariableName(String variableName) {
599: this .variableName = variableName.trim();
600: this .originalName = this .variableName;
601: }
602:
603: /**
604: * Sets the variable value.
605: *
606: * @param variableValue the new variable value
607: */
608: public void setVariableValue(String variableValue) {
609: this .variableValue = variableValue;
610: }
611:
612: /*
613: * (non-Javadoc)
614: * @see org.eclipse.jface.dialogs.Dialog#isResizable()
615: */
616: protected boolean isResizable() {
617: return true;
618: }
619:
620: }
|