blob: 1d6467e64e6b6c46549c0cc8a43f95873797ff36 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
import com.android.SdkConstants;
import com.android.ide.common.resources.configuration.ResourceQualifier;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode;
import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard.Values;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
/**
* Second page of the {@link NewXmlFileWizard}.
* <p>
* This page is used for choosing the current configuration or specific resource
* folder.
*/
public class ChooseConfigurationPage extends WizardPage {
private Values mValues;
private Text mWsFolderPathTextField;
private ConfigurationSelector mConfigSelector;
private boolean mInternalWsFolderPathUpdate;
private boolean mInternalConfigSelectorUpdate;
/** Absolute destination folder root, e.g. "/res/" */
static final String RES_FOLDER_ABS = AdtConstants.WS_RESOURCES + AdtConstants.WS_SEP;
/** Relative destination folder root, e.g. "res/" */
static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AdtConstants.WS_SEP;
/**
* Create the wizard.
*
* @param values value object holding current wizard state
*/
public ChooseConfigurationPage(NewXmlFileWizard.Values values) {
super("chooseConfig");
mValues = values;
setTitle("Choose Configuration Folder");
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (visible) {
if (mValues.folderPath != null) {
mWsFolderPathTextField.setText(mValues.folderPath);
}
}
}
@Override
public void createControl(Composite parent) {
// This UI is maintained with WindowBuilder.
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout(2, false /* makeColumnsEqualWidth */));
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
// label before configuration selector
Label label = new Label(composite, SWT.NONE);
label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
label.setText("Optional: Choose a specific configuration to limit the XML to:");
// configuration selector
mConfigSelector = new ConfigurationSelector(composite, SelectorMode.DEFAULT);
GridData gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
gd.verticalAlignment = SWT.FILL;
gd.horizontalAlignment = SWT.FILL;
gd.horizontalSpan = 2;
gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
mConfigSelector.setLayoutData(gd);
mConfigSelector.setOnChangeListener(new ConfigurationChangeListener());
// Folder name: [text]
String tooltip = "The folder where the file will be generated, relative to the project.";
Label separator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
GridData gdSeparator = new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1);
gdSeparator.heightHint = 10;
separator.setLayoutData(gdSeparator);
Label folderLabel = new Label(composite, SWT.NONE);
folderLabel.setText("Folder:");
folderLabel.setToolTipText(tooltip);
mWsFolderPathTextField = new Text(composite, SWT.BORDER);
mWsFolderPathTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mWsFolderPathTextField.setToolTipText(tooltip);
mWsFolderPathTextField.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
onWsFolderPathUpdated();
}
});
setControl(composite);
mConfigSelector.setConfiguration(mValues.configuration);
}
/**
* Callback called when the Folder text field is changed, either programmatically
* or by the user.
*/
private void onWsFolderPathUpdated() {
if (mInternalWsFolderPathUpdate) {
return;
}
String wsFolderPath = mWsFolderPathTextField.getText();
// This is a custom path, we need to sanitize it.
// First it should start with "/res/". Then we need to make sure there are no
// relative paths, things like "../" or "./" or even "//".
wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$
wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$
wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$
// We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
mInternalWsFolderPathUpdate = true;
mWsFolderPathTextField.setText(wsFolderPath);
mInternalWsFolderPathUpdate = false;
}
mValues.folderPath = wsFolderPath;
if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
int pos = wsFolderPath.indexOf(AdtConstants.WS_SEP_CHAR);
if (pos >= 0) {
wsFolderPath = wsFolderPath.substring(0, pos);
}
String[] folderSegments = wsFolderPath.split(SdkConstants.RES_QUALIFIER_SEP);
if (folderSegments.length > 0) {
String folderName = folderSegments[0];
// update config selector
mInternalConfigSelectorUpdate = true;
mConfigSelector.setConfiguration(folderSegments);
mInternalConfigSelectorUpdate = false;
IWizardPage previous = ((NewXmlFileWizard) getWizard()).getPreviousPage(this);
if (previous instanceof NewXmlFileCreationPage) {
NewXmlFileCreationPage p = (NewXmlFileCreationPage) previous;
p.selectTypeFromFolder(folderName);
}
}
}
validatePage();
}
/**
* Callback called when the configuration has changed in the {@link ConfigurationSelector}.
*/
private class ConfigurationChangeListener implements Runnable {
@Override
public void run() {
if (mInternalConfigSelectorUpdate) {
return;
}
resetFolderPath(true /*validate*/);
}
}
/**
* Reset the current Folder path based on the UI selection
* @param validate if true, force a call to {@link #validatePage()}.
*/
private void resetFolderPath(boolean validate) {
TypeInfo type = mValues.type;
if (type != null) {
mConfigSelector.getConfiguration(mValues.configuration);
StringBuilder sb = new StringBuilder(RES_FOLDER_ABS);
sb.append(mValues.configuration.getFolderName(type.getResFolderType()));
mInternalWsFolderPathUpdate = true;
String newPath = sb.toString();
mValues.folderPath = newPath;
mWsFolderPathTextField.setText(newPath);
mInternalWsFolderPathUpdate = false;
if (validate) {
validatePage();
}
}
}
/**
* Returns the destination folder path relative to the project or an empty string.
*
* @return the currently edited folder
*/
public String getWsFolderPath() {
return mWsFolderPathTextField == null ? "" : mWsFolderPathTextField.getText(); //$NON-NLS-1$
}
/**
* Validates the fields, displays errors and warnings.
* Enables the finish button if there are no errors.
*/
private void validatePage() {
String error = null;
String warning = null;
// -- validate folder configuration
if (error == null) {
ConfigurationState state = mConfigSelector.getState();
if (state == ConfigurationState.INVALID_CONFIG) {
ResourceQualifier qual = mConfigSelector.getInvalidQualifier();
if (qual != null) {
error =
String.format("The qualifier '%1$s' is invalid in the folder configuration.",
qual.getName());
}
} else if (state == ConfigurationState.REGION_WITHOUT_LANGUAGE) {
error = "The Region qualifier requires the Language qualifier.";
}
}
// -- validate generated path
if (error == null) {
String wsFolderPath = getWsFolderPath();
if (!wsFolderPath.startsWith(RES_FOLDER_ABS)) {
error = String.format("Target folder must start with %1$s.", RES_FOLDER_ABS);
}
}
// -- validate destination file doesn't exist
if (error == null) {
IFile file = mValues.getDestinationFile();
if (file != null && file.exists()) {
warning = "The destination file already exists";
}
}
// -- update UI & enable finish if there's no error
setPageComplete(error == null);
if (error != null) {
setMessage(error, IMessageProvider.ERROR);
} else if (warning != null) {
setMessage(warning, IMessageProvider.WARNING);
} else {
setErrorMessage(null);
setMessage(null);
}
}
}