/** * <copyright> * * Copyright (c) 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation * * </copyright> * * $Id$ */ package org.eclipse.gmf.runtime.emf.ui.preferences; import java.io.File; import org.eclipse.core.resources.IPathVariableManager; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.URI; import org.eclipse.gmf.runtime.emf.core.internal.resources.PathmapManager; import org.eclipse.gmf.runtime.emf.ui.internal.MslUIPlugin; import org.eclipse.gmf.runtime.emf.ui.internal.l10n.EMFUIMessages; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; /** * Modal dialog for manipulation of path variables for modeling pathmaps. This * dialog supports both creation of new path variables, using the * {@link #openNew(Shell)} method, and editing of existing path variables, using * the {@link #openEdit(Shell, String, String)} method. In either case, if the * user successfully closes the dialog (i.e., hits the OK button), then the name * and location of the variable are accessed via the {@link #getVariableName()} * and {@link #getVariableLocation()} methods, respectively. * <p> * Note that this dialog intentionally differs from that utilized by the Linked * Resources preference page because GMF's pathmaps apply more constraints to * path variables, in particular that: * </p> * <ul> * <li>a pathmap variable name must be a valid URI segment, because it is used * as is in <tt>pathmap://</tt> URIs</li> * <li>a pathmap variable must reference a folder, not a file</li> * </ul> * * @author Christian W. Damus (cdamus) */ class NewPathVariableDialog extends TitleAreaDialog { private String variableName; private IPath variableLocation; private Text nameText; private Text locationText; private String initialName; private String initialLocation; private final IPathVariableManager pathMgr; private final String plainMsg; private final PathmapsPreferencePage page; /** * Not instantiable by clients. * * @param page * the path maps preference page that I serve * @param name * the current name of the variable to be edited, or * <code>null</code> if creating a new path variable * @param location * the current location of the variable to be edited, or * <code>null</code> if creating a new path variable */ private NewPathVariableDialog(PathmapsPreferencePage page, String name, String location) { super(page.getShell()); this.page = page; this.initialName = name; this.initialLocation = location; if (name != null) { // edit mode plainMsg = EMFUIMessages.PathVariableDialog_editMessage; } else { // new mode plainMsg = EMFUIMessages.PathVariableDialog_newMessage; } pathMgr = ResourcesPlugin.getWorkspace().getPathVariableManager(); } /** * Opens the path-variable editing dialog in creation mode, to create a new * path variable. * * @param page * the path maps preference page that the dialog serves * @return the dialog instance, from which the path variable data can be * extracted, if the user closed it with the "OK" button; * <code>null</code>, otherwise (i.e., if the user canceled) */ public static NewPathVariableDialog openNew(PathmapsPreferencePage page) { NewPathVariableDialog dlg = new NewPathVariableDialog(page, null, null); return (dlg.open() == IDialogConstants.OK_ID) ? dlg : null; } /** * Opens the path-variable editing dialog in edit mode, to modify an * existing path variable. Clients must account for the possibility that the * returned variable name may differ from the specified <code>name</code>, * because users may rename variables. * * @param page * the path maps preference page that the dialog serves * @return the dialog instance, from which the path variable data can be * extracted, if the user closed it with the "OK" button; * <code>null</code>, otherwise (i.e., if the user canceled) */ public static NewPathVariableDialog openEdit(PathmapsPreferencePage page, String name, String location) { NewPathVariableDialog dlg = new NewPathVariableDialog(page, name, location); return (dlg.open() == IDialogConstants.OK_ID) ? dlg : null; } /** * Sets the dialog's window title according to whether it is in creation or * edit mode. */ protected void configureShell(Shell newShell) { super.configureShell(newShell); if (initialName != null) { // edit mode newShell.setText(EMFUIMessages.PathVariableDialog_editTitle); } else { // new mode newShell.setText(EMFUIMessages.PathVariableDialog_newTitle); } } protected Control createDialogArea(Composite parent) { Composite result = (Composite) super.createDialogArea(parent); initializeDialogUnits(result); Composite composite = new Composite(result, SWT.NONE); composite.setLayout(new GridLayout(3, false)); GridData data = null; data = new GridData(GridData.FILL_BOTH); data.grabExcessHorizontalSpace = true; data.grabExcessHorizontalSpace = true; data.horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); data.verticalIndent = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); composite.setLayoutData(data); Label label = new Label(composite, SWT.LEFT); label.setText(EMFUIMessages.PathVariableDialog_nameLabel); data = new GridData(SWT.BEGINNING); label.setLayoutData(data); nameText = new Text(composite, SWT.BORDER); data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; nameText.setLayoutData(data); if (initialName != null) { nameText.setText(initialName); } // blank to occupy the upper-right corner new Label(composite, SWT.None); label = new Label(composite, SWT.NONE); label.setText(EMFUIMessages.PathVariableDialog_locationLabel); data = new GridData(SWT.BEGINNING); label.setLayoutData(data); // force left-to-right orientation because file paths are always LTR locationText = new Text(composite, SWT.BORDER | SWT.LEFT_TO_RIGHT); data = new GridData(GridData.FILL_HORIZONTAL); data.grabExcessHorizontalSpace = true; locationText.setLayoutData(data); if (initialLocation != null) { locationText.setText(initialLocation); } Button browseButton = new Button(composite, SWT.PUSH); browseButton.setText(EMFUIMessages.PathVariableDialog_browseButton); setButtonLayoutData(browseButton); browseButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { DirectoryDialog dlg = new DirectoryDialog(getShell()); dlg.setText(EMFUIMessages.PathVariableDialog_browseDialogTitle); dlg .setMessage(EMFUIMessages.PathVariableDialog_browseDialogMessage); String folder = dlg.open(); if (folder != null) { locationText.setText(folder); } } }); ModifyListener l = new ModifyListener() { public void modifyText(ModifyEvent e) { validateInputs(); } }; nameText.addModifyListener(l); locationText.addModifyListener(l); if (initialName != null) { // edit mode setTitle(EMFUIMessages.PathVariableDialog_editTitle); // select the location field text and set focus to it locationText.setSelection(0, locationText.getText().length()); locationText.setFocus(); } else { // new mode setTitle(EMFUIMessages.PathVariableDialog_newTitle); } setMessage(plainMsg); return result; } /** * Initially disables the OK button, because in either creation or edit * mode, the user will have to input some data before it can hit OK. */ protected void createButtonsForButtonBar(Composite parent) { super.createButtonsForButtonBar(parent); // initially, the OK button is disabled because we haven't yet any input getButton(IDialogConstants.OK_ID).setEnabled(false); } /** * Validates the current values of the variable name and location entry * fields. A warning or error message is shown in the title area, if * appropriate, and the OK button is disabled if any input is invalid. * Moreover, in edit mode, the OK button is disabled if the user hasn't * changed either the variable name or the location. */ private void validateInputs() { IStatus status = Status.OK_STATUS; boolean isError = false; String name = nameText.getText(); String location = locationText.getText(); boolean hasName = name.length() > 0; boolean hasLocation = location.length() > 0; if (hasName && !name.equals(initialName)) { status = validateName(name); } if (!status.isOK()) { isError = true; setMessage(status.getMessage(), IMessageProvider.ERROR); } else if (hasLocation && !location.equals(initialLocation)) { status = validateLocation(new Path(location)); if (!status.isOK()) { isError = status.getSeverity() >= IStatus.ERROR; setMessage(status.getMessage(), isError ? IMessageProvider.ERROR : IMessageProvider.WARNING); } } if (status.isOK()) { setMessage(plainMsg); } if (initialName != null) { // edit mode. Check that either the name or the location is changed if (name.equals(initialName) && location.equals(initialLocation)) { // force OK button to be disabled hasName = false; } } // dialog not complete if error or missing an input getButton(IDialogConstants.OK_ID).setEnabled( !isError && hasName && hasLocation); } /** * Validates the specified variable <code>name</code>. * * @param name * the variable name to validate * @return the result of validation, which may be OK or may contain a * warning or error message to display in the title area */ private IStatus validateName(String name) { IStatus result; if (pathMgr.isDefined(name) && !page.isRemoved(name) || PathmapManager.isRegisteredPathVariable(name) || page.isAdded(name)) { result = new Status(IStatus.ERROR, MslUIPlugin.getPluginId(), EMFUIMessages.PathVariableDialog_alreadyDefined_ERROR_); } else if (!URI.validSegment(name)) { result = new Status(IStatus.ERROR, MslUIPlugin.getPluginId(), EMFUIMessages.PathVariableDialog_invalidSegment_ERROR_); } else { result = pathMgr.validateName(name); } return result; } /** * Validates the specified variable <code>location</code>. * * @param location * the variable location to validate * @return the result of validation, which may be OK or may contain a * warning or error message to display in the title area */ private IStatus validateLocation(IPath location) { IStatus result; File file = location.toFile(); if (file.exists() && !file.isDirectory()) { result = new Status(IStatus.ERROR, MslUIPlugin.getPluginId(), EMFUIMessages.PathVariableDialog_notFolder_ERROR_); } else if (!file.exists()) { result = new Status(IStatus.ERROR, MslUIPlugin.getPluginId(), EMFUIMessages.PathVariableDialog_noSuchFolder_ERROR_); } else { result = pathMgr.validateValue(location); } if (result.isOK()) { if (page.isLocationDefined(location)) { result = new Status(IStatus.WARNING, MslUIPlugin .getPluginId(), EMFUIMessages.PathVariableDialog_sameLocation_WARN_); } } return result; } /** * Stores the variable name and location for retrieval by the client when * the user succeeds in closing the dialog by pressing the OK button. * * @see #getVariableName() * @see #getVariableLocation() */ protected void okPressed() { variableName = nameText.getText(); variableLocation = new Path(locationText.getText()); super.okPressed(); } /** * Obtains the path variable name entered by the user. * * @return the path variable name */ String getVariableName() { return variableName; } /** * Obtains the path variable location entered by the user. * * @return the path variable location */ IPath getVariableLocation() { return variableLocation; } }