diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ChooseConfigurationPage.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ChooseConfigurationPage.java | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ChooseConfigurationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ChooseConfigurationPage.java new file mode 100644 index 000000000..1d6467e64 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ChooseConfigurationPage.java @@ -0,0 +1,282 @@ +/* + * 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); + } + } +} |