diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile')
4 files changed, 0 insertions, 2529 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/AddTranslationDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/AddTranslationDialog.java deleted file mode 100644 index 0301b80fe..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/AddTranslationDialog.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (C) 2012 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 static com.android.SdkConstants.FD_RES; -import static com.android.SdkConstants.FD_RES_VALUES; -import static com.android.SdkConstants.RES_QUALIFIER_SEP; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.res2.ValueXmlHelper; -import com.android.ide.common.resources.LocaleManager; -import com.android.ide.common.resources.ResourceItem; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.FlagManager; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewManager; -import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; -import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; -import com.android.resources.ResourceType; -import com.google.common.base.Charsets; -import com.google.common.collect.Maps; - -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.jface.viewers.ArrayContentProvider; -import org.eclipse.jface.viewers.CellEditor; -import org.eclipse.jface.viewers.CellLabelProvider; -import org.eclipse.jface.viewers.ColumnViewer; -import org.eclipse.jface.viewers.EditingSupport; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.TableViewerColumn; -import org.eclipse.jface.viewers.TextCellEditor; -import org.eclipse.jface.viewers.ViewerCell; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.ControlListener; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.PlatformUI; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; - -/** - * Dialog which adds a new translation to the project - */ -public class AddTranslationDialog extends Dialog implements ControlListener, SelectionListener, - TraverseListener { - private static final int KEY_COLUMN = 0; - private static final int DEFAULT_TRANSLATION_COLUMN = 1; - private static final int NEW_TRANSLATION_COLUMN = 2; - private final FolderConfiguration mConfiguration = new FolderConfiguration(); - private final IProject mProject; - private String mTarget; - private boolean mIgnore; - private Map<String, String> mTranslations; - private Set<String> mExistingLanguages; - private String mSelectedLanguage; - private String mSelectedRegion; - - private Table mTable; - private Combo mLanguageCombo; - private Combo mRegionCombo; - private ImageControl mFlag; - private Label mFile; - private Button mOkButton; - private Composite mErrorPanel; - private Label mErrorLabel; - private MyTableViewer mTableViewer; - - /** - * Creates the dialog. - * @param parentShell the parent shell - * @param project the project to add translations into - */ - public AddTranslationDialog(Shell parentShell, IProject project) { - super(parentShell); - setShellStyle(SWT.CLOSE | SWT.RESIZE | SWT.TITLE); - mProject = project; - } - - @Override - protected Control createDialogArea(Composite parent) { - Composite container = (Composite) super.createDialogArea(parent); - GridLayout gl_container = new GridLayout(6, false); - gl_container.horizontalSpacing = 0; - container.setLayout(gl_container); - - Label languageLabel = new Label(container, SWT.NONE); - languageLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - languageLabel.setText("Language:"); - mLanguageCombo = new Combo(container, SWT.READ_ONLY); - GridData gd_mLanguageCombo = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); - gd_mLanguageCombo.widthHint = 150; - mLanguageCombo.setLayoutData(gd_mLanguageCombo); - - Label regionLabel = new Label(container, SWT.NONE); - GridData gd_regionLabel = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1); - gd_regionLabel.horizontalIndent = 10; - regionLabel.setLayoutData(gd_regionLabel); - regionLabel.setText("Region:"); - mRegionCombo = new Combo(container, SWT.READ_ONLY); - GridData gd_mRegionCombo = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); - gd_mRegionCombo.widthHint = 150; - mRegionCombo.setLayoutData(gd_mRegionCombo); - mRegionCombo.setEnabled(false); - - mFlag = new ImageControl(container, SWT.NONE, null); - mFlag.setDisposeImage(false); - GridData gd_mFlag = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); - gd_mFlag.exclude = true; - gd_mFlag.widthHint = 32; - gd_mFlag.horizontalIndent = 3; - mFlag.setLayoutData(gd_mFlag); - - mFile = new Label(container, SWT.NONE); - mFile.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - - mTableViewer = new MyTableViewer(container, SWT.BORDER | SWT.FULL_SELECTION); - mTable = mTableViewer.getTable(); - mTable.setEnabled(false); - mTable.setLinesVisible(true); - mTable.setHeaderVisible(true); - mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 6, 2)); - mTable.addControlListener(this); - mTable.addTraverseListener(this); - // If you have difficulty opening up this form in WindowBuilder and it complains about - // the next line, change the type of the mTableViewer field and the above - // constructor call from MyTableViewer to TableViewer - TableViewerColumn keyViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE); - TableColumn keyColumn = keyViewerColumn.getColumn(); - keyColumn.setWidth(100); - keyColumn.setText("Key"); - TableViewerColumn defaultViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE); - TableColumn defaultColumn = defaultViewerColumn.getColumn(); - defaultColumn.setWidth(200); - defaultColumn.setText("Default"); - TableViewerColumn translationViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE); - TableColumn translationColumn = translationViewerColumn.getColumn(); - translationColumn.setWidth(200); - translationColumn.setText("New Translation"); - - mErrorPanel = new Composite(container, SWT.NONE); - GridData gd_mErrorLabel = new GridData(SWT.FILL, SWT.CENTER, false, false, 6, 1); - gd_mErrorLabel.exclude = true; - mErrorPanel.setLayoutData(gd_mErrorLabel); - - translationViewerColumn.setEditingSupport(new TranslationEditingSupport(mTableViewer)); - - fillLanguages(); - fillRegions(); - fillStrings(); - updateColumnWidths(); - validatePage(); - - mLanguageCombo.addSelectionListener(this); - mRegionCombo.addSelectionListener(this); - - return container; - } - - /** Populates the table with keys and default strings */ - private void fillStrings() { - ResourceManager manager = ResourceManager.getInstance(); - ProjectResources resources = manager.getProjectResources(mProject); - mExistingLanguages = resources.getLanguages(); - - Collection<ResourceItem> items = resources.getResourceItemsOfType(ResourceType.STRING); - - ResourceItem[] array = items.toArray(new ResourceItem[items.size()]); - Arrays.sort(array); - - // TODO: Read in the actual XML files providing the default keys here - // (they can be obtained via ResourceItem.getSourceFileList()) - // such that we can read all the attributes associated with each - // item, and if it defines translatable=false, or the filename is - // donottranslate.xml, we can ignore it, and in other cases just - // duplicate all the attributes (such as "formatted=true", or other - // local conventions such as "product=tablet", or "msgid="123123123", - // etc.) - - mTranslations = Maps.newHashMapWithExpectedSize(items.size()); - IBaseLabelProvider labelProvider = new CellLabelProvider() { - @Override - public void update(ViewerCell cell) { - Object element = cell.getElement(); - int index = cell.getColumnIndex(); - ResourceItem item = (ResourceItem) element; - switch (index) { - case KEY_COLUMN: { - // Key - cell.setText(item.getName()); - return; - } - case DEFAULT_TRANSLATION_COLUMN: { - // Default translation - ResourceValue value = item.getResourceValue(ResourceType.STRING, - mConfiguration, false); - - if (value != null) { - cell.setText(value.getValue()); - return; - } - break; - } - case NEW_TRANSLATION_COLUMN: { - // New translation - String translation = mTranslations.get(item.getName()); - if (translation != null) { - cell.setText(translation); - return; - } - break; - } - default: - assert false : index; - } - cell.setText(""); - } - }; - - mTableViewer.setLabelProvider(labelProvider); - mTableViewer.setContentProvider(new ArrayContentProvider()); - mTableViewer.setInput(array); - } - - /** Populate the languages dropdown */ - private void fillLanguages() { - List<String> languageCodes = LocaleManager.getLanguageCodes(); - List<String> labels = new ArrayList<String>(); - for (String code : languageCodes) { - labels.add(code + ": " + LocaleManager.getLanguageName(code)); //$NON-NLS-1$ - } - Collections.sort(labels); - labels.add(0, "(Select)"); - mLanguageCombo.setItems(labels.toArray(new String[labels.size()])); - mLanguageCombo.select(0); - } - - /** Populate the regions dropdown */ - private void fillRegions() { - // TODO: When you switch languages, offer some "default" usable options. For example, - // when you choose English, offer the countries that use English, and so on. Unfortunately - // we don't have good data about this, we'd just need to hardcode a few common cases. - List<String> regionCodes = LocaleManager.getRegionCodes(); - List<String> labels = new ArrayList<String>(); - for (String code : regionCodes) { - labels.add(code + ": " + LocaleManager.getRegionName(code)); //$NON-NLS-1$ - } - Collections.sort(labels); - labels.add(0, "Any"); - mRegionCombo.setItems(labels.toArray(new String[labels.size()])); - mRegionCombo.select(0); - } - - /** React to resizing by distributing the space evenly between the last two columns */ - private void updateColumnWidths() { - Rectangle r = mTable.getClientArea(); - int availableWidth = r.width; - // Distribute all available space to the last two columns - int columnCount = mTable.getColumnCount(); - for (int i = 0; i < columnCount; i++) { - TableColumn column = mTable.getColumn(i); - availableWidth -= column.getWidth(); - } - if (availableWidth != 0) { - TableColumn column = mTable.getColumn(DEFAULT_TRANSLATION_COLUMN); - column.setWidth(column.getWidth() + availableWidth / 2); - column = mTable.getColumn(NEW_TRANSLATION_COLUMN); - column.setWidth(column.getWidth() + availableWidth / 2 + availableWidth % 2); - } - } - - @Override - protected void createButtonsForButtonBar(Composite parent) { - mOkButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, - // Don't make the OK button default as in most dialogs, since when you press - // Return thinking you might edit a value it dismisses the dialog instead - false); - createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); - mOkButton.setEnabled(false); - - validatePage(); - } - - /** - * Return the initial size of the dialog. - */ - @Override - protected Point getInitialSize() { - return new Point(800, 600); - } - - private void updateTarget() { - if (mSelectedLanguage == null) { - mTarget = null; - mFile.setText(""); - } else { - String folder = FD_RES + '/' + FD_RES_VALUES + RES_QUALIFIER_SEP + mSelectedLanguage; - if (mSelectedRegion != null) { - folder = folder + RES_QUALIFIER_SEP + 'r' + mSelectedRegion; - } - mTarget = folder + "/strings.xml"; //$NON-NLS-1$ - mFile.setText(String.format("Creating %1$s", mTarget)); - } - } - - private void updateFlag() { - if (mSelectedLanguage == null) { - // Nothing selected - ((GridData) mFlag.getLayoutData()).exclude = true; - } else { - FlagManager manager = FlagManager.get(); - Image flag = manager.getFlag(mSelectedLanguage, mSelectedRegion); - if (flag != null) { - ((GridData) mFlag.getLayoutData()).exclude = false; - mFlag.setImage(flag); - } - } - - mFlag.getParent().layout(true); - mFlag.getParent().redraw(); - } - - /** Actually create the new translation file and write it to disk */ - private void createTranslation() { - List<String> keys = new ArrayList<String>(mTranslations.keySet()); - Collections.sort(keys); - - StringBuilder sb = new StringBuilder(keys.size() * 120); - sb.append("<resources>\n\n"); //$NON-NLS-1$ - for (String key : keys) { - String value = mTranslations.get(key); - if (value == null || value.trim().isEmpty()) { - continue; - } - sb.append(" <string name=\""); //$NON-NLS-1$ - sb.append(key); - sb.append("\">"); //$NON-NLS-1$ - sb.append(ValueXmlHelper.escapeResourceString(value)); - sb.append("</string>\n"); //$NON-NLS-1$ - } - sb.append("\n</resources>"); //$NON-NLS-1$ - - IFile file = mProject.getFile(mTarget); - - try { - IContainer parent = file.getParent(); - AdtUtils.ensureExists(parent); - InputStream source = new ByteArrayInputStream(sb.toString().getBytes(Charsets.UTF_8)); - file.create(source, true, new NullProgressMonitor()); - AdtPlugin.openFile(file, null, true /*showEditorTab*/); - - // Ensure that the project resources updates itself to notice the new language. - // In theory, this shouldn't be necessary. - ResourceManager manager = ResourceManager.getInstance(); - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - IFolder folder = root.getFolder(parent.getFullPath()); - manager.getResourceFolder(folder); - RenderPreviewManager.bumpRevision(); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - } - - private void validatePage() { - if (mOkButton == null) { // Early initialization - return; - } - - String message = null; - - if (mSelectedLanguage == null) { - message = "Select a language"; - } else if (mExistingLanguages.contains(mSelectedLanguage)) { - if (mSelectedRegion == null) { - message = String.format("%1$s is already translated in this project", - LocaleManager.getLanguageName(mSelectedLanguage)); - } else { - ResourceManager manager = ResourceManager.getInstance(); - ProjectResources resources = manager.getProjectResources(mProject); - SortedSet<String> regions = resources.getRegions(mSelectedLanguage); - if (regions.contains(mSelectedRegion)) { - message = String.format("%1$s (%2$s) is already translated in this project", - LocaleManager.getLanguageName(mSelectedLanguage), - LocaleManager.getRegionName(mSelectedRegion)); - } - } - } else { - // Require all strings to be translated? No, some of these may not - // be translatable (e.g. translatable=false, defined in donottranslate.xml, etc.) - //int missing = mTable.getItemCount() - mTranslations.values().size(); - //if (missing > 0) { - // message = String.format("Missing %1$d translations", missing); - //} - } - - boolean valid = message == null; - mTable.setEnabled(message == null); - mOkButton.setEnabled(valid); - showError(message); - } - - private void showError(String error) { - GridData data = (GridData) mErrorPanel.getLayoutData(); - - boolean show = error != null; - if (show == data.exclude) { - if (show) { - if (mErrorLabel == null) { - mErrorPanel.setLayout(new GridLayout(2, false)); - IWorkbench workbench = PlatformUI.getWorkbench(); - ISharedImages sharedImages = workbench.getSharedImages(); - String iconName = ISharedImages.IMG_OBJS_ERROR_TSK; - Image image = sharedImages.getImage(iconName); - @SuppressWarnings("unused") - ImageControl icon = new ImageControl(mErrorPanel, SWT.NONE, image); - - mErrorLabel = new Label(mErrorPanel, SWT.NONE); - mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, - 1, 1)); - } - mErrorLabel.setText(error); - } - data.exclude = !show; - mErrorPanel.getParent().layout(true); - } - } - - @Override - protected void okPressed() { - mTableViewer.applyEditorValue(); - - super.okPressed(); - createTranslation(); - } - - // ---- Implements ControlListener ---- - - @Override - public void controlMoved(ControlEvent e) { - } - - @Override - public void controlResized(ControlEvent e) { - if (mIgnore) { - return; - } - - updateColumnWidths(); - } - - // ---- Implements SelectionListener ---- - - @Override - public void widgetSelected(SelectionEvent e) { - if (mIgnore) { - return; - } - - Object source = e.getSource(); - if (source == mLanguageCombo) { - try { - mIgnore = true; - mRegionCombo.select(0); - mSelectedRegion = null; - } finally { - mIgnore = false; - } - - int languageIndex = mLanguageCombo.getSelectionIndex(); - if (languageIndex == 0) { - mSelectedLanguage = null; - mRegionCombo.setEnabled(false); - } else { - // This depends on the label format - mSelectedLanguage = mLanguageCombo.getItem(languageIndex).substring(0, 2); - mRegionCombo.setEnabled(true); - } - - updateTarget(); - updateFlag(); - } else if (source == mRegionCombo) { - int regionIndex = mRegionCombo.getSelectionIndex(); - if (regionIndex == 0) { - mSelectedRegion = null; - } else { - mSelectedRegion = mRegionCombo.getItem(regionIndex).substring(0, 2); - } - - updateTarget(); - updateFlag(); - } - - try { - mIgnore = true; - validatePage(); - } finally { - mIgnore = false; - } - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - - // ---- TraverseListener ---- - - @Override - public void keyTraversed(TraverseEvent e) { - // If you press Return and we're not cell editing, start editing the current row - if (e.detail == SWT.TRAVERSE_RETURN && !mTableViewer.isCellEditorActive()) { - int index = mTable.getSelectionIndex(); - if (index != -1) { - Object next = mTable.getItem(index).getData(); - mTableViewer.editElement(next, NEW_TRANSLATION_COLUMN); - } - } - } - - /** Editing support for the translation column */ - private class TranslationEditingSupport extends EditingSupport { - /** - * When true, setValue is being called as part of a default action - * (e.g. Return), not due to focus loss - */ - private boolean mDefaultAction; - - private TranslationEditingSupport(ColumnViewer viewer) { - super(viewer); - } - - @Override - protected void setValue(Object element, Object value) { - ResourceItem item = (ResourceItem) element; - mTranslations.put(item.getName(), value.toString()); - mTableViewer.update(element, null); - validatePage(); - - // If the user is pressing Return to finish editing a value (which is - // not the only way this method can get called - for example, if you click - // outside the cell while editing, the focus loss will also result in - // this method getting called), then mDefaultAction is true, and we automatically - // start editing the next row. - if (mDefaultAction) { - mTable.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if (!mTable.isDisposed() && !mTableViewer.isCellEditorActive()) { - int index = mTable.getSelectionIndex(); - if (index != -1 && index < mTable.getItemCount() - 1) { - Object next = mTable.getItem(index + 1).getData(); - mTableViewer.editElement(next, NEW_TRANSLATION_COLUMN); - } - } - } - }); - } - } - - @Override - protected Object getValue(Object element) { - ResourceItem item = (ResourceItem) element; - String value = mTranslations.get(item.getName()); - if (value == null) { - return ""; - } - return value; - } - - @Override - protected CellEditor getCellEditor(Object element) { - return new TextCellEditor(mTable) { - @Override - protected void handleDefaultSelection(SelectionEvent event) { - try { - mDefaultAction = true; - super.handleDefaultSelection(event); - } finally { - mDefaultAction = false; - } - } - }; - } - - @Override - protected boolean canEdit(Object element) { - return true; - } - } - - private class MyTableViewer extends TableViewer { - public MyTableViewer(Composite parent, int style) { - super(parent, style); - } - - // Make this public so we can call it to ensure values are applied before the dialog - // is dismissed in {@link #okPressed} - @Override - public void applyEditorValue() { - super.applyEditorValue(); - } - } -} 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 deleted file mode 100644 index 1d6467e64..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/ChooseConfigurationPage.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 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); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java deleted file mode 100644 index 28fb8c032..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java +++ /dev/null @@ -1,1163 +0,0 @@ -/* - * Copyright (C) 2008 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 static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW; -import static com.android.SdkConstants.LINEAR_LAYOUT; -import static com.android.SdkConstants.RES_QUALIFIER_SEP; -import static com.android.SdkConstants.SCROLL_VIEW; -import static com.android.SdkConstants.VALUE_FILL_PARENT; -import static com.android.SdkConstants.VALUE_MATCH_PARENT; -import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP_CHAR; -import static com.android.ide.eclipse.adt.internal.wizards.newxmlfile.ChooseConfigurationPage.RES_FOLDER_ABS; - -import com.android.SdkConstants; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider; -import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; -import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.ProjectCombo; -import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; -import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; -import com.android.resources.ResourceFolderType; -import com.android.sdklib.IAndroidTarget; -import com.android.utils.Pair; -import com.android.utils.SdkUtils; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jface.dialogs.IMessageProvider; -import org.eclipse.jface.viewers.ArrayContentProvider; -import org.eclipse.jface.viewers.ColumnLabelProvider; -import org.eclipse.jface.viewers.IBaseLabelProvider; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.TableViewer; -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.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Combo; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.part.FileEditorInput; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - -/** - * This is the first page of the {@link NewXmlFileWizard} which provides the ability to create - * skeleton XML resources files for Android projects. - * <p/> - * This page is used to select the project, resource type and file name. - */ -class NewXmlFileCreationPage extends WizardPage { - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - // Ensure the initial focus is in the Name field; you usually don't need - // to edit the default text field (the project name) - if (visible && mFileNameTextField != null) { - mFileNameTextField.setFocus(); - } - - validatePage(); - } - - /** - * Information on one type of resource that can be created (e.g. menu, pref, layout, etc.) - */ - static class TypeInfo { - private final String mUiName; - private final ResourceFolderType mResFolderType; - private final String mTooltip; - private final Object mRootSeed; - private ArrayList<String> mRoots = new ArrayList<String>(); - private final String mXmlns; - private final String mDefaultAttrs; - private final String mDefaultRoot; - private final int mTargetApiLevel; - - public TypeInfo(String uiName, - String tooltip, - ResourceFolderType resFolderType, - Object rootSeed, - String defaultRoot, - String xmlns, - String defaultAttrs, - int targetApiLevel) { - mUiName = uiName; - mResFolderType = resFolderType; - mTooltip = tooltip; - mRootSeed = rootSeed; - mDefaultRoot = defaultRoot; - mXmlns = xmlns; - mDefaultAttrs = defaultAttrs; - mTargetApiLevel = targetApiLevel; - } - - /** Returns the UI name for the resource type. Unique. Never null. */ - String getUiName() { - return mUiName; - } - - /** Returns the tooltip for the resource type. Can be null. */ - String getTooltip() { - return mTooltip; - } - - /** - * Returns the name of the {@link ResourceFolderType}. - * Never null but not necessarily unique, - * e.g. two types use {@link ResourceFolderType#XML}. - */ - String getResFolderName() { - return mResFolderType.getName(); - } - - /** - * Returns the matching {@link ResourceFolderType}. - * Never null but not necessarily unique, - * e.g. two types use {@link ResourceFolderType#XML}. - */ - ResourceFolderType getResFolderType() { - return mResFolderType; - } - - /** - * Returns the seed used to fill the root element values. - * The seed might be either a String, a String array, an {@link ElementDescriptor}, - * a {@link DocumentDescriptor} or null. - */ - Object getRootSeed() { - return mRootSeed; - } - - /** - * Returns the default root element that should be selected by default. Can be - * null. - * - * @param project the associated project, or null if not known - */ - String getDefaultRoot(IProject project) { - return mDefaultRoot; - } - - /** - * Returns the list of all possible root elements for the resource type. - * This can be an empty ArrayList but not null. - * <p/> - * TODO: the root list SHOULD depend on the currently selected project, to include - * custom classes. - */ - ArrayList<String> getRoots() { - return mRoots; - } - - /** - * If the generated resource XML file requires an "android" XMLNS, this should be set - * to {@link SdkConstants#NS_RESOURCES}. When it is null, no XMLNS is generated. - */ - String getXmlns() { - return mXmlns; - } - - /** - * When not null, this represent extra attributes that must be specified in the - * root element of the generated XML file. When null, no extra attributes are inserted. - * - * @param project the project to get the attributes for - * @param root the selected root element string, never null - */ - String getDefaultAttrs(IProject project, String root) { - return mDefaultAttrs; - } - - /** - * When not null, represents an extra string that should be written inside - * the element when constructed - * - * @param project the project to get the child content for - * @param root the chosen root element - * @return a string to be written inside the root element, or null if nothing - */ - String getChild(IProject project, String root) { - return null; - } - - /** - * The minimum API level required by the current SDK target to support this feature. - * - * @return the minimum API level - */ - public int getTargetApiLevel() { - return mTargetApiLevel; - } - } - - /** - * TypeInfo, information for each "type" of file that can be created. - */ - private static final TypeInfo[] sTypes = { - new TypeInfo( - "Layout", // UI name - "An XML file that describes a screen layout.", // tooltip - ResourceFolderType.LAYOUT, // folder type - AndroidTargetData.DESCRIPTOR_LAYOUT, // root seed - LINEAR_LAYOUT, // default root - SdkConstants.NS_RESOURCES, // xmlns - "", // not used, see below - 1 // target API level - ) { - - @Override - String getDefaultRoot(IProject project) { - // TODO: Use GridLayout by default for new SDKs - // (when we've ironed out all the usability issues) - //Sdk currentSdk = Sdk.getCurrent(); - //if (project != null && currentSdk != null) { - // IAndroidTarget target = currentSdk.getTarget(project); - // // fill_parent was renamed match_parent in API level 8 - // if (target != null && target.getVersion().getApiLevel() >= 13) { - // return GRID_LAYOUT; - // } - //} - - return LINEAR_LAYOUT; - }; - - // The default attributes must be determined dynamically since whether - // we use match_parent or fill_parent depends on the API level of the - // project - @Override - String getDefaultAttrs(IProject project, String root) { - Sdk currentSdk = Sdk.getCurrent(); - String fill = VALUE_FILL_PARENT; - if (currentSdk != null) { - IAndroidTarget target = currentSdk.getTarget(project); - // fill_parent was renamed match_parent in API level 8 - if (target != null && target.getVersion().getApiLevel() >= 8) { - fill = VALUE_MATCH_PARENT; - } - } - - // Only set "vertical" orientation of LinearLayouts by default; - // for GridLayouts for example we want to rely on the real default - // of the layout - String size = String.format( - "android:layout_width=\"%1$s\"\n" //$NON-NLS-1$ - + "android:layout_height=\"%2$s\"", //$NON-NLS-1$ - fill, fill); - if (LINEAR_LAYOUT.equals(root)) { - return "android:orientation=\"vertical\"\n" + size; //$NON-NLS-1$ - } else { - return size; - } - } - - @Override - String getChild(IProject project, String root) { - // Create vertical linear layouts inside new scroll views - if (SCROLL_VIEW.equals(root) || HORIZONTAL_SCROLL_VIEW.equals(root)) { - return " <LinearLayout " //$NON-NLS-1$ - + getDefaultAttrs(project, root).replace('\n', ' ') - + " android:orientation=\"vertical\"" //$NON-NLS-1$ - + "></LinearLayout>\n"; //$NON-NLS-1$ - } - return null; - } - }, - new TypeInfo("Values", // UI name - "An XML file with simple values: colors, strings, dimensions, etc.", // tooltip - ResourceFolderType.VALUES, // folder type - SdkConstants.TAG_RESOURCES, // root seed - null, // default root - null, // xmlns - null, // default attributes - 1 // target API level - ), - new TypeInfo("Drawable", // UI name - "An XML file that describes a drawable.", // tooltip - ResourceFolderType.DRAWABLE, // folder type - AndroidTargetData.DESCRIPTOR_DRAWABLE, // root seed - null, // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 1 // target API level - ), - new TypeInfo("Menu", // UI name - "An XML file that describes an menu.", // tooltip - ResourceFolderType.MENU, // folder type - SdkConstants.TAG_MENU, // root seed - null, // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 1 // target API level - ), - new TypeInfo("Color List", // UI name - "An XML file that describes a color state list.", // tooltip - ResourceFolderType.COLOR, // folder type - AndroidTargetData.DESCRIPTOR_COLOR, // root seed - "selector", //$NON-NLS-1$ // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 1 // target API level - ), - new TypeInfo("Property Animation", // UI name - "An XML file that describes a property animation", // tooltip - ResourceFolderType.ANIMATOR, // folder type - AndroidTargetData.DESCRIPTOR_ANIMATOR, // root seed - "set", //$NON-NLS-1$ // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 11 // target API level - ), - new TypeInfo("Tween Animation", // UI name - "An XML file that describes a tween animation.", // tooltip - ResourceFolderType.ANIM, // folder type - AndroidTargetData.DESCRIPTOR_ANIM, // root seed - "set", //$NON-NLS-1$ // default root - null, // xmlns - null, // default attributes - 1 // target API level - ), - new TypeInfo("AppWidget Provider", // UI name - "An XML file that describes a widget provider.", // tooltip - ResourceFolderType.XML, // folder type - AndroidTargetData.DESCRIPTOR_APPWIDGET_PROVIDER, // root seed - null, // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 3 // target API level - ), - new TypeInfo("Preference", // UI name - "An XML file that describes preferences.", // tooltip - ResourceFolderType.XML, // folder type - AndroidTargetData.DESCRIPTOR_PREFERENCES, // root seed - SdkConstants.CLASS_NAME_PREFERENCE_SCREEN, // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 1 // target API level - ), - new TypeInfo("Searchable", // UI name - "An XML file that describes a searchable.", // tooltip - ResourceFolderType.XML, // folder type - AndroidTargetData.DESCRIPTOR_SEARCHABLE, // root seed - null, // default root - SdkConstants.NS_RESOURCES, // xmlns - null, // default attributes - 1 // target API level - ), - // Still missing: Interpolator, Raw and Mipmap. Raw should probably never be in - // this menu since it's not often used for creating XML files. - }; - - private NewXmlFileWizard.Values mValues; - private ProjectCombo mProjectButton; - private Text mFileNameTextField; - private Combo mTypeCombo; - private IStructuredSelection mInitialSelection; - private ResourceFolderType mInitialFolderType; - private boolean mInternalTypeUpdate; - private TargetChangeListener mSdkTargetChangeListener; - private Table mRootTable; - private TableViewer mRootTableViewer; - - // --- UI creation --- - - /** - * Constructs a new {@link NewXmlFileCreationPage}. - * <p/> - * Called by {@link NewXmlFileWizard#createMainPage}. - */ - protected NewXmlFileCreationPage(String pageName, NewXmlFileWizard.Values values) { - super(pageName); - mValues = values; - setPageComplete(false); - } - - public void setInitialSelection(IStructuredSelection initialSelection) { - mInitialSelection = initialSelection; - } - - public void setInitialFolderType(ResourceFolderType initialType) { - mInitialFolderType = initialType; - } - - /** - * Called by the parent Wizard to create the UI for this Wizard Page. - * - * {@inheritDoc} - * - * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) - */ - @Override - @SuppressWarnings("unused") // SWT constructors have side effects, they aren't unused - 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 type radios - Label typeLabel = new Label(composite, SWT.NONE); - typeLabel.setText("Resource Type:"); - - mTypeCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY); - mTypeCombo.setToolTipText("What type of resource would you like to create?"); - mTypeCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - if (mInitialFolderType != null) { - mTypeCombo.setEnabled(false); - } - mTypeCombo.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - TypeInfo type = getSelectedType(); - if (type != null) { - onSelectType(type); - } - } - }); - - // separator - Label separator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); - GridData gd2 = new GridData(GridData.GRAB_HORIZONTAL); - gd2.horizontalAlignment = SWT.FILL; - gd2.horizontalSpan = 2; - separator.setLayoutData(gd2); - - // Project: [button] - String tooltip = "The Android Project where the new resource file will be created."; - Label projectLabel = new Label(composite, SWT.NONE); - projectLabel.setText("Project:"); - projectLabel.setToolTipText(tooltip); - - ProjectChooserHelper helper = - new ProjectChooserHelper(getShell(), null /* filter */); - - mProjectButton = new ProjectCombo(helper, composite, mValues.project); - mProjectButton.setToolTipText(tooltip); - mProjectButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mProjectButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - IProject project = mProjectButton.getSelectedProject(); - if (project != mValues.project) { - changeProject(project); - } - }; - }); - - // Filename: [text] - Label fileLabel = new Label(composite, SWT.NONE); - fileLabel.setText("File:"); - fileLabel.setToolTipText("The name of the resource file to create."); - - mFileNameTextField = new Text(composite, SWT.BORDER); - mFileNameTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mFileNameTextField.setToolTipText(tooltip); - mFileNameTextField.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - mValues.name = mFileNameTextField.getText(); - validatePage(); - } - }); - - // separator - Label rootSeparator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); - GridData gd = new GridData(GridData.GRAB_HORIZONTAL); - gd.horizontalAlignment = SWT.FILL; - gd.horizontalSpan = 2; - rootSeparator.setLayoutData(gd); - - // Root Element: - // [TableViewer] - Label rootLabel = new Label(composite, SWT.NONE); - rootLabel.setText("Root Element:"); - new Label(composite, SWT.NONE); - - mRootTableViewer = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION); - mRootTable = mRootTableViewer.getTable(); - GridData tableGridData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1); - tableGridData.heightHint = 200; - mRootTable.setLayoutData(tableGridData); - - setControl(composite); - - // Update state the first time - setErrorMessage(null); - setMessage(null); - - initializeFromSelection(mInitialSelection); - updateAvailableTypes(); - initializeFromFixedType(); - initializeRootValues(); - installTargetChangeListener(); - - initialSelectType(); - validatePage(); - } - - private void initialSelectType() { - TypeInfo[] types = (TypeInfo[]) mTypeCombo.getData(); - int typeIndex = getTypeComboIndex(mValues.type); - if (typeIndex == -1) { - typeIndex = 0; - } else { - assert mValues.type == types[typeIndex]; - } - mTypeCombo.select(typeIndex); - onSelectType(types[typeIndex]); - updateRootCombo(types[typeIndex]); - } - - private void installTargetChangeListener() { - mSdkTargetChangeListener = new TargetChangeListener() { - @Override - public IProject getProject() { - return mValues.project; - } - - @Override - public void reload() { - if (mValues.project != null) { - changeProject(mValues.project); - } - } - }; - - AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); - } - - @Override - public void dispose() { - - if (mSdkTargetChangeListener != null) { - AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener); - mSdkTargetChangeListener = null; - } - - super.dispose(); - } - - /** - * Returns the selected root element string, if any. - * - * @return The selected root element string or null. - */ - public String getRootElement() { - int index = mRootTable.getSelectionIndex(); - if (index >= 0) { - Object[] roots = (Object[]) mRootTableViewer.getInput(); - return roots[index].toString(); - } - return null; - } - - /** - * Called by {@link NewXmlFileWizard} to initialize the page with the selection - * received by the wizard -- typically the current user workbench selection. - * <p/> - * Things we expect to find out from the selection: - * <ul> - * <li>The project name, valid if it's an android nature.</li> - * <li>The current folder, valid if it's a folder under /res</li> - * <li>An existing filename, in which case the user will be asked whether to override it.</li> - * </ul> - * <p/> - * The selection can also be set to a {@link Pair} of {@link IProject} and a workspace - * resource path (where the resource path does not have to exist yet, such as res/anim/). - * - * @param selection The selection when the wizard was initiated. - */ - private boolean initializeFromSelection(IStructuredSelection selection) { - if (selection == null) { - return false; - } - - // Find the best match in the element list. In case there are multiple selected elements - // select the one that provides the most information and assign them a score, - // e.g. project=1 + folder=2 + file=4. - IProject targetProject = null; - String targetWsFolderPath = null; - String targetFileName = null; - int targetScore = 0; - for (Object element : selection.toList()) { - if (element instanceof IAdaptable) { - IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class); - IProject project = res != null ? res.getProject() : null; - - // Is this an Android project? - try { - if (project == null || !project.hasNature(AdtConstants.NATURE_DEFAULT)) { - continue; - } - } catch (CoreException e) { - // checking the nature failed, ignore this resource - continue; - } - - int score = 1; // we have a valid project at least - - IPath wsFolderPath = null; - String fileName = null; - assert res != null; // Eclipse incorrectly thinks res could be null, so tell it no - if (res.getType() == IResource.FOLDER) { - wsFolderPath = res.getProjectRelativePath(); - } else if (res.getType() == IResource.FILE) { - if (SdkUtils.endsWithIgnoreCase(res.getName(), DOT_XML)) { - fileName = res.getName(); - } - wsFolderPath = res.getParent().getProjectRelativePath(); - } - - // Disregard this folder selection if it doesn't point to /res/something - if (wsFolderPath != null && - wsFolderPath.segmentCount() > 1 && - SdkConstants.FD_RESOURCES.equals(wsFolderPath.segment(0))) { - score += 2; - } else { - wsFolderPath = null; - fileName = null; - } - - score += fileName != null ? 4 : 0; - - if (score > targetScore) { - targetScore = score; - targetProject = project; - targetWsFolderPath = wsFolderPath != null ? wsFolderPath.toString() : null; - targetFileName = fileName; - } - } else if (element instanceof Pair<?,?>) { - // Pair of Project/String - @SuppressWarnings("unchecked") - Pair<IProject,String> pair = (Pair<IProject,String>)element; - targetScore = 1; - targetProject = pair.getFirst(); - targetWsFolderPath = pair.getSecond(); - targetFileName = ""; - } - } - - if (targetProject == null) { - // Try to figure out the project from the active editor - IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); - if (window != null) { - IWorkbenchPage page = window.getActivePage(); - if (page != null) { - IEditorPart activeEditor = page.getActiveEditor(); - if (activeEditor instanceof AndroidXmlEditor) { - Object input = ((AndroidXmlEditor) activeEditor).getEditorInput(); - if (input instanceof FileEditorInput) { - FileEditorInput fileInput = (FileEditorInput) input; - targetScore = 1; - IFile file = fileInput.getFile(); - targetProject = file.getProject(); - IPath path = file.getParent().getProjectRelativePath(); - targetWsFolderPath = path != null ? path.toString() : null; - } - } - } - } - } - - if (targetProject == null) { - // If we didn't find a default project based on the selection, check how many - // open Android projects we can find in the current workspace. If there's only - // one, we'll just select it by default. - IJavaProject[] projects = AdtUtils.getOpenAndroidProjects(); - if (projects != null && projects.length == 1) { - targetScore = 1; - targetProject = projects[0].getProject(); - } - } - - // Now set the UI accordingly - if (targetScore > 0) { - mValues.project = targetProject; - mValues.folderPath = targetWsFolderPath; - mProjectButton.setSelectedProject(targetProject); - mFileNameTextField.setText(targetFileName != null ? targetFileName : ""); //$NON-NLS-1$ - - // If the current selection context corresponds to a specific file type, - // select it. - if (targetWsFolderPath != null) { - int pos = targetWsFolderPath.lastIndexOf(WS_SEP_CHAR); - if (pos >= 0) { - targetWsFolderPath = targetWsFolderPath.substring(pos + 1); - } - String[] folderSegments = targetWsFolderPath.split(RES_QUALIFIER_SEP); - if (folderSegments.length > 0) { - mValues.configuration = FolderConfiguration.getConfig(folderSegments); - String folderName = folderSegments[0]; - selectTypeFromFolder(folderName); - } - } - } - - return true; - } - - private void initializeFromFixedType() { - if (mInitialFolderType != null) { - for (TypeInfo type : sTypes) { - if (type.getResFolderType() == mInitialFolderType) { - mValues.type = type; - updateFolderPath(type); - break; - } - } - } - } - - /** - * Given a folder name, such as "drawable", select the corresponding type in - * the dropdown. - */ - void selectTypeFromFolder(String folderName) { - List<TypeInfo> matches = new ArrayList<TypeInfo>(); - boolean selected = false; - - TypeInfo selectedType = getSelectedType(); - for (TypeInfo type : sTypes) { - if (type.getResFolderName().equals(folderName)) { - matches.add(type); - selected |= type == selectedType; - } - } - - if (matches.size() == 1) { - // If there's only one match, select it if it's not already selected - if (!selected) { - selectType(matches.get(0)); - } - } else if (matches.size() > 1) { - // There are multiple type candidates for this folder. This can happen - // for /res/xml for example. Check to see if one of them is currently - // selected. If yes, leave the selection unchanged. If not, deselect all type. - if (!selected) { - selectType(null); - } - } else { - // Nothing valid was selected. - selectType(null); - } - } - - /** - * Initialize the root values of the type infos based on the current framework values. - */ - private void initializeRootValues() { - IProject project = mValues.project; - for (TypeInfo type : sTypes) { - // Clear all the roots for this type - ArrayList<String> roots = type.getRoots(); - if (roots.size() > 0) { - roots.clear(); - } - - // depending of the type of the seed, initialize the root in different ways - Object rootSeed = type.getRootSeed(); - - if (rootSeed instanceof String) { - // The seed is a single string, Add it as-is. - roots.add((String) rootSeed); - } else if (rootSeed instanceof String[]) { - // The seed is an array of strings. Add them as-is. - for (String value : (String[]) rootSeed) { - roots.add(value); - } - } else if (rootSeed instanceof Integer && project != null) { - // The seed is a descriptor reference defined in AndroidTargetData.DESCRIPTOR_* - // In this case add all the children element descriptors defined, recursively, - // and avoid infinite recursion by keeping track of what has already been added. - - // Note: if project is null, the root list will be empty since it has been - // cleared above. - - // get the AndroidTargetData from the project - IAndroidTarget target = null; - AndroidTargetData data = null; - - target = Sdk.getCurrent().getTarget(project); - if (target == null) { - // A project should have a target. The target can be missing if the project - // is an old project for which a target hasn't been affected or if the - // target no longer exists in this SDK. Simply log the error and dismiss. - - AdtPlugin.log(IStatus.INFO, - "NewXmlFile wizard: no platform target for project %s", //$NON-NLS-1$ - project.getName()); - continue; - } else { - data = Sdk.getCurrent().getTargetData(target); - - if (data == null) { - // We should have both a target and its data. - // However if the wizard is invoked whilst the platform is still being - // loaded we can end up in a weird case where we have a target but it - // doesn't have any data yet. - // Lets log a warning and silently ignore this root. - - AdtPlugin.log(IStatus.INFO, - "NewXmlFile wizard: no data for target %s, project %s", //$NON-NLS-1$ - target.getName(), project.getName()); - continue; - } - } - - IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed); - ElementDescriptor descriptor = provider.getDescriptor(); - if (descriptor != null) { - HashSet<ElementDescriptor> visited = new HashSet<ElementDescriptor>(); - initRootElementDescriptor(roots, descriptor, visited); - } - - // Sort alphabetically. - Collections.sort(roots); - } - } - } - - /** - * Helper method to recursively insert all XML names for the given {@link ElementDescriptor} - * into the roots array list. Keeps track of visited nodes to avoid infinite recursion. - * Also avoids inserting the top {@link DocumentDescriptor} which is generally synthetic - * and not a valid root element. - */ - private void initRootElementDescriptor(ArrayList<String> roots, - ElementDescriptor desc, HashSet<ElementDescriptor> visited) { - if (!(desc instanceof DocumentDescriptor)) { - String xmlName = desc.getXmlName(); - if (xmlName != null && xmlName.length() > 0) { - roots.add(xmlName); - } - } - - visited.add(desc); - - for (ElementDescriptor child : desc.getChildren()) { - if (!visited.contains(child)) { - initRootElementDescriptor(roots, child, visited); - } - } - } - - /** - * Changes mProject to the given new project and update the UI accordingly. - * <p/> - * Note that this does not check if the new project is the same as the current one - * on purpose, which allows a project to be updated when its target has changed or - * when targets are loaded in the background. - */ - private void changeProject(IProject newProject) { - mValues.project = newProject; - - // enable types based on new API level - updateAvailableTypes(); - initialSelectType(); - - // update the folder name based on API level - updateFolderPath(mValues.type); - - // update the Type with the new descriptors. - initializeRootValues(); - - // update the combo - updateRootCombo(mValues.type); - - validatePage(); - } - - private void onSelectType(TypeInfo type) { - // Do nothing if this is an internal modification or if the widget has been - // deselected. - if (mInternalTypeUpdate) { - return; - } - - mValues.type = type; - - if (type == null) { - return; - } - - // update the combo - updateRootCombo(type); - - // update the folder path - updateFolderPath(type); - - validatePage(); - } - - /** Updates the selected type in the type dropdown control */ - private void setSelectedType(TypeInfo type) { - TypeInfo[] types = (TypeInfo[]) mTypeCombo.getData(); - if (types != null) { - for (int i = 0, n = types.length; i < n; i++) { - if (types[i] == type) { - mTypeCombo.select(i); - break; - } - } - } - } - - /** Returns the selected type in the type dropdown control */ - private TypeInfo getSelectedType() { - int index = mTypeCombo.getSelectionIndex(); - if (index != -1) { - TypeInfo[] types = (TypeInfo[]) mTypeCombo.getData(); - return types[index]; - } - - return null; - } - - /** Returns the selected index in the type dropdown control */ - private int getTypeComboIndex(TypeInfo type) { - TypeInfo[] types = (TypeInfo[]) mTypeCombo.getData(); - for (int i = 0, n = types.length; i < n; i++) { - if (type == types[i]) { - return i; - } - } - - return -1; - } - - /** Updates the folder path to reflect the given type */ - private void updateFolderPath(TypeInfo type) { - String wsFolderPath = mValues.folderPath; - String newPath = null; - FolderConfiguration config = mValues.configuration; - ResourceQualifier qual = config.getInvalidQualifier(); - if (qual == null) { - // The configuration is valid. Reformat the folder path using the canonical - // value from the configuration. - newPath = RES_FOLDER_ABS + config.getFolderName(type.getResFolderType()); - } else { - // The configuration is invalid. We still update the path but this time - // do it manually on the string. - if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { - wsFolderPath = wsFolderPath.replaceFirst( - "^(" + RES_FOLDER_ABS +")[^-]*(.*)", //$NON-NLS-1$ //$NON-NLS-2$ - "\\1" + type.getResFolderName() + "\\2"); //$NON-NLS-1$ //$NON-NLS-2$ - } else { - newPath = RES_FOLDER_ABS + config.getFolderName(type.getResFolderType()); - } - } - - if (newPath != null && !newPath.equals(wsFolderPath)) { - mValues.folderPath = newPath; - } - } - - /** - * Helper method that fills the values of the "root element" combo box based - * on the currently selected type radio button. Also disables the combo is there's - * only one choice. Always select the first root element for the given type. - * - * @param type The currently selected {@link TypeInfo}, or null - */ - private void updateRootCombo(TypeInfo type) { - IBaseLabelProvider labelProvider = new ColumnLabelProvider() { - @Override - public Image getImage(Object element) { - return IconFactory.getInstance().getIcon(element.toString()); - } - }; - mRootTableViewer.setContentProvider(new ArrayContentProvider()); - mRootTableViewer.setLabelProvider(labelProvider); - - if (type != null) { - // get the list of roots. The list can be empty but not null. - ArrayList<String> roots = type.getRoots(); - mRootTableViewer.setInput(roots.toArray()); - - int index = 0; // default is to select the first one - String defaultRoot = type.getDefaultRoot(mValues.project); - if (defaultRoot != null) { - index = roots.indexOf(defaultRoot); - } - mRootTable.select(index < 0 ? 0 : index); - mRootTable.showSelection(); - } - } - - /** - * Helper method to select the current type in the type dropdown - * - * @param type The TypeInfo matching the radio button to selected or null to deselect them all. - */ - private void selectType(TypeInfo type) { - mInternalTypeUpdate = true; - mValues.type = type; - if (type == null) { - if (mTypeCombo.getSelectionIndex() != -1) { - mTypeCombo.deselect(mTypeCombo.getSelectionIndex()); - } - } else { - setSelectedType(type); - } - updateRootCombo(type); - mInternalTypeUpdate = false; - } - - /** - * Add the available types in the type combobox, based on whether they are available - * for the current SDK. - * <p/> - * A type is available either if: - * - if mProject is null, API level 1 is considered valid - * - if mProject is !null, the project->target->API must be >= to the type's API level. - */ - private void updateAvailableTypes() { - IProject project = mValues.project; - IAndroidTarget target = project != null ? Sdk.getCurrent().getTarget(project) : null; - int currentApiLevel = 1; - if (target != null) { - currentApiLevel = target.getVersion().getApiLevel(); - } - - List<String> items = new ArrayList<String>(sTypes.length); - List<TypeInfo> types = new ArrayList<TypeInfo>(sTypes.length); - for (int i = 0, n = sTypes.length; i < n; i++) { - TypeInfo type = sTypes[i]; - if (type.getTargetApiLevel() <= currentApiLevel) { - items.add(type.getUiName()); - types.add(type); - } - } - mTypeCombo.setItems(items.toArray(new String[items.size()])); - mTypeCombo.setData(types.toArray(new TypeInfo[types.size()])); - } - - /** - * 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 type - TypeInfo type = mValues.type; - if (error == null) { - if (type == null) { - error = "One of the types must be selected (e.g. layout, values, etc.)"; - } - } - - // -- validate project - if (mValues.project == null) { - error = "Please select an Android project."; - } - - // -- validate type API level - if (error == null) { - IAndroidTarget target = Sdk.getCurrent().getTarget(mValues.project); - int currentApiLevel = 1; - if (target != null) { - currentApiLevel = target.getVersion().getApiLevel(); - } - - assert type != null; - if (type.getTargetApiLevel() > currentApiLevel) { - error = "The API level of the selected type (e.g. AppWidget, etc.) is not " + - "compatible with the API level of the project."; - } - } - - // -- validate filename - if (error == null) { - String fileName = mValues.getFileName(); - assert type != null; - ResourceFolderType folderType = type.getResFolderType(); - error = ResourceNameValidator.create(true, folderType).isValid(fileName); - } - - // -- 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); - } - } - - /** - * Returns the {@link TypeInfo} for the given {@link ResourceFolderType}, or null if - * not found - * - * @param folderType the {@link ResourceFolderType} to look for - * @return the corresponding {@link TypeInfo} - */ - static TypeInfo getTypeInfo(ResourceFolderType folderType) { - for (TypeInfo typeInfo : sTypes) { - if (typeInfo.getResFolderType() == folderType) { - return typeInfo; - } - } - - return null; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java deleted file mode 100644 index 16cd7b355..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (C) 2008 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 static com.android.SdkConstants.FQCN_GRID_LAYOUT; -import static com.android.SdkConstants.GRID_LAYOUT; - -import com.android.SdkConstants; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.xml.XmlFormatStyle; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlFormatPreferences; -import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewManager; -import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.project.SupportLibraryHelper; -import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo; -import com.android.resources.ResourceFolderType; -import com.android.utils.Pair; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Path; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.Region; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.INewWizard; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.PartInitException; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; - -/** - * The "New Android XML File Wizard" provides the ability to create skeleton XML - * resources files for Android projects. - * <p/> - * The wizard has one page, {@link NewXmlFileCreationPage}, used to select the project, - * the resource folder, resource type and file name. It then creates the XML file. - */ -public class NewXmlFileWizard extends Wizard implements INewWizard { - /** The XML header to write at the top of the XML file */ - public static final String XML_HEADER_LINE = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$ - - private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$ - - protected static final String MAIN_PAGE_NAME = "newAndroidXmlFilePage"; //$NON-NLS-1$ - - private NewXmlFileCreationPage mMainPage; - private ChooseConfigurationPage mConfigPage; - private Values mValues; - - @Override - public void init(IWorkbench workbench, IStructuredSelection selection) { - setHelpAvailable(false); // TODO have help - setWindowTitle("New Android XML File"); - setImageDescriptor(); - - mValues = new Values(); - mMainPage = createMainPage(mValues); - mMainPage.setTitle("New Android XML File"); - mMainPage.setDescription("Creates a new Android XML file."); - mMainPage.setInitialSelection(selection); - - mConfigPage = new ChooseConfigurationPage(mValues); - - // Trigger a check to see if the SDK needs to be reloaded (which will - // invoke onSdkLoaded asynchronously as needed). - AdtPlugin.getDefault().refreshSdk(); - } - - /** - * Creates the wizard page. - * <p/> - * Please do NOT override this method. - * <p/> - * This is protected so that it can be overridden by unit tests. - * However the contract of this class is private and NO ATTEMPT will be made - * to maintain compatibility between different versions of the plugin. - */ - protected NewXmlFileCreationPage createMainPage(NewXmlFileWizard.Values values) { - return new NewXmlFileCreationPage(MAIN_PAGE_NAME, values); - } - - // -- Methods inherited from org.eclipse.jface.wizard.Wizard -- - // - // The Wizard class implements most defaults and boilerplate code needed by - // IWizard - - /** - * Adds pages to this wizard. - */ - @Override - public void addPages() { - addPage(mMainPage); - addPage(mConfigPage); - - } - - /** - * Performs any actions appropriate in response to the user having pressed - * the Finish button, or refuse if finishing now is not permitted: here, it - * actually creates the workspace project and then switch to the Java - * perspective. - * - * @return True - */ - @Override - public boolean performFinish() { - final Pair<IFile, IRegion> created = createXmlFile(); - if (created == null) { - return false; - } else { - // Open the file - // This has to be delayed in order for focus handling to work correctly - AdtPlugin.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - IFile file = created.getFirst(); - IRegion region = created.getSecond(); - try { - IEditorPart editor = AdtPlugin.openFile(file, null, - false /*showEditorTab*/); - if (editor instanceof AndroidXmlEditor) { - final AndroidXmlEditor xmlEditor = (AndroidXmlEditor)editor; - if (!xmlEditor.hasMultiplePages()) { - xmlEditor.show(region.getOffset(), region.getLength(), - true /* showEditorTab */); - } - } - } catch (PartInitException e) { - AdtPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$ - file.getFullPath().toString()); - } - }}); - - return true; - } - } - - // -- Custom Methods -- - - private Pair<IFile, IRegion> createXmlFile() { - IFile file = mValues.getDestinationFile(); - TypeInfo type = mValues.type; - if (type == null) { - // this is not expected to happen - String name = file.getFullPath().toString(); - AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing type", name); //$NON-NLS-1$ - return null; - } - String xmlns = type.getXmlns(); - String root = mMainPage.getRootElement(); - if (root == null) { - // this is not expected to happen - AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing root element", //$NON-NLS-1$ - file.toString()); - return null; - } - - String attrs = type.getDefaultAttrs(mValues.project, root); - String child = type.getChild(mValues.project, root); - return createXmlFile(file, xmlns, root, attrs, child, type.getResFolderType()); - } - - /** Creates a new file using the given root element, namespace and root attributes */ - private static Pair<IFile, IRegion> createXmlFile(IFile file, String xmlns, - String root, String rootAttributes, String child, ResourceFolderType folderType) { - String name = file.getFullPath().toString(); - boolean need_delete = false; - - if (file.exists()) { - if (!AdtPlugin.displayPrompt("New Android XML File", - String.format("Do you want to overwrite the file %1$s ?", name))) { - // abort if user selects cancel. - return null; - } - need_delete = true; - } else { - AdtUtils.createWsParentDirectory(file.getParent()); - } - - StringBuilder sb = new StringBuilder(XML_HEADER_LINE); - - if (folderType == ResourceFolderType.LAYOUT && root.equals(GRID_LAYOUT)) { - IProject project = file.getParent().getProject(); - int minSdk = ManifestInfo.get(project).getMinSdkVersion(); - if (minSdk < 14) { - root = SupportLibraryHelper.getTagFor(project, FQCN_GRID_LAYOUT); - if (root.equals(FQCN_GRID_LAYOUT)) { - root = GRID_LAYOUT; - } - } - } - - sb.append('<').append(root); - if (xmlns != null) { - sb.append('\n').append(" xmlns:android=\"").append(xmlns).append('"'); //$NON-NLS-1$ - } - - if (rootAttributes != null) { - sb.append("\n "); //$NON-NLS-1$ - sb.append(rootAttributes.replace("\n", "\n ")); //$NON-NLS-1$ //$NON-NLS-2$ - } - - sb.append(">\n"); //$NON-NLS-1$ - - if (child != null) { - sb.append(child); - } - - boolean autoFormat = AdtPrefs.getPrefs().getUseCustomXmlFormatter(); - - // Insert an indented caret. Since the markup here will be reformatted, we need to - // insert text tokens that the formatter will preserve, which we can then turn back - // into indentation and a caret offset: - final String indentToken = "${indent}"; //$NON-NLS-1$ - final String caretToken = "${caret}"; //$NON-NLS-1$ - sb.append(indentToken); - sb.append(caretToken); - if (!autoFormat) { - sb.append('\n'); - } - - sb.append("</").append(root).append(">\n"); //$NON-NLS-1$ //$NON-NLS-2$ - - EclipseXmlFormatPreferences formatPrefs = EclipseXmlFormatPreferences.create(); - String fileContents; - if (!autoFormat) { - fileContents = sb.toString(); - } else { - XmlFormatStyle style = EclipseXmlPrettyPrinter.getForFolderType(folderType); - fileContents = EclipseXmlPrettyPrinter.prettyPrint(sb.toString(), formatPrefs, - style, null /*lineSeparator*/); - } - - // Remove marker tokens and replace them with whitespace - fileContents = fileContents.replace(indentToken, formatPrefs.getOneIndentUnit()); - int caretOffset = fileContents.indexOf(caretToken); - if (caretOffset != -1) { - fileContents = fileContents.replace(caretToken, ""); //$NON-NLS-1$ - } - - String error = null; - try { - byte[] buf = fileContents.getBytes("UTF8"); //$NON-NLS-1$ - InputStream stream = new ByteArrayInputStream(buf); - if (need_delete) { - file.delete(IResource.KEEP_HISTORY | IResource.FORCE, null /*monitor*/); - } - file.create(stream, true /*force*/, null /*progress*/); - IRegion region = caretOffset != -1 ? new Region(caretOffset, 0) : null; - - // If you introduced a new locale, or new screen variations etc, ensure that - // the list of render previews is updated if necessary - if (file.getParent().getName().indexOf('-') != -1 - && (folderType == ResourceFolderType.LAYOUT - || folderType == ResourceFolderType.VALUES)) { - RenderPreviewManager.bumpRevision(); - } - - return Pair.of(file, region); - } catch (UnsupportedEncodingException e) { - error = e.getMessage(); - } catch (CoreException e) { - error = e.getMessage(); - } - - error = String.format("Failed to generate %1$s: %2$s", name, error); - AdtPlugin.displayError("New Android XML File", error); - return null; - } - - /** - * Returns true if the New XML Wizard can create new files of the given - * {@link ResourceFolderType} - * - * @param folderType the folder type to create a file for - * @return true if this wizard can create new files for the given folder type - */ - public static boolean canCreateXmlFile(ResourceFolderType folderType) { - TypeInfo typeInfo = NewXmlFileCreationPage.getTypeInfo(folderType); - return typeInfo != null && (typeInfo.getDefaultRoot(null /*project*/) != null || - typeInfo.getRootSeed() instanceof String); - } - - /** - * Creates a new XML file using the template according to the given folder type - * - * @param project the project to create the file in - * @param file the file to be created - * @param folderType the type of folder to look up a template for - * @return the created file - */ - public static Pair<IFile, IRegion> createXmlFile(IProject project, IFile file, - ResourceFolderType folderType) { - TypeInfo type = NewXmlFileCreationPage.getTypeInfo(folderType); - String xmlns = type.getXmlns(); - String root = type.getDefaultRoot(project); - if (root == null) { - root = type.getRootSeed().toString(); - } - String attrs = type.getDefaultAttrs(project, root); - return createXmlFile(file, xmlns, root, attrs, null, folderType); - } - - /** - * Returns an image descriptor for the wizard logo. - */ - private void setImageDescriptor() { - ImageDescriptor desc = IconFactory.getInstance().getImageDescriptor(PROJECT_LOGO_LARGE); - setDefaultPageImageDescriptor(desc); - } - - /** - * Specific New XML File wizard tied to the {@link ResourceFolderType#LAYOUT} type - */ - public static class NewLayoutWizard extends NewXmlFileWizard { - /** Creates a new {@link NewLayoutWizard} */ - public NewLayoutWizard() { - } - - @Override - public void init(IWorkbench workbench, IStructuredSelection selection) { - super.init(workbench, selection); - setWindowTitle("New Android Layout XML File"); - super.mMainPage.setTitle("New Android Layout XML File"); - super.mMainPage.setDescription("Creates a new Android Layout XML file."); - super.mMainPage.setInitialFolderType(ResourceFolderType.LAYOUT); - } - } - - /** - * Specific New XML File wizard tied to the {@link ResourceFolderType#VALUES} type - */ - public static class NewValuesWizard extends NewXmlFileWizard { - /** Creates a new {@link NewValuesWizard} */ - public NewValuesWizard() { - } - - @Override - public void init(IWorkbench workbench, IStructuredSelection selection) { - super.init(workbench, selection); - setWindowTitle("New Android Values XML File"); - super.mMainPage.setTitle("New Android Values XML File"); - super.mMainPage.setDescription("Creates a new Android Values XML file."); - super.mMainPage.setInitialFolderType(ResourceFolderType.VALUES); - } - } - - /** Value object which holds the current state of the wizard pages */ - public static class Values { - /** The currently selected project, or null */ - public IProject project; - /** The root name of the XML file to create, or null */ - public String name; - /** The type of XML file to create */ - public TypeInfo type; - /** The path within the project to create the new file in */ - public String folderPath; - /** The currently chosen configuration */ - public FolderConfiguration configuration = new FolderConfiguration(); - - /** - * Returns the destination filename or an empty string. - * - * @return the filename, never null. - */ - public String getFileName() { - String fileName; - if (name == null) { - fileName = ""; //$NON-NLS-1$ - } else { - fileName = name.trim(); - if (fileName.length() > 0 && fileName.indexOf('.') == -1) { - fileName = fileName + SdkConstants.DOT_XML; - } - } - - return fileName; - } - - /** - * Returns a {@link IFile} for the destination file. - * <p/> - * Returns null if the project, filename or folder are invalid and the - * destination file cannot be determined. - * <p/> - * The {@link IFile} is a resource. There might or might not be an - * actual real file. - * - * @return an {@link IFile} for the destination file - */ - public IFile getDestinationFile() { - String fileName = getFileName(); - if (project != null && folderPath != null && folderPath.length() > 0 - && fileName.length() > 0) { - IPath dest = new Path(folderPath).append(fileName); - IFile file = project.getFile(dest); - return file; - } - return null; - } - } -} |