diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java | 1251 |
1 files changed, 1251 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java new file mode 100644 index 000000000..17336ad2d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java @@ -0,0 +1,1251 @@ +/* + * 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.assetstudio; + +import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.DEFAULT_LAUNCHER_ICON; +import static java.awt.image.BufferedImage.TYPE_INT_ARGB; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.assetstudiolib.ActionBarIconGenerator; +import com.android.assetstudiolib.GraphicGenerator; +import com.android.assetstudiolib.GraphicGenerator.Shape; +import com.android.assetstudiolib.LauncherIconGenerator; +import com.android.assetstudiolib.MenuIconGenerator; +import com.android.assetstudiolib.NotificationIconGenerator; +import com.android.assetstudiolib.TabIconGenerator; +import com.android.assetstudiolib.TextRenderUtil; +import com.android.assetstudiolib.Util; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState.SourceType; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils; +import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; +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.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.FontDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Slider; +import org.eclipse.swt.widgets.Text; + +import java.awt.Paint; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.imageio.ImageIO; + +/** + * This is normally page 2 of a Create New Asset Set wizard, unless we can offer actions + * to create a specific asset type, in which case we skip page 1. On this page the user + * gets to configure the parameters of the asset, and see a preview. + */ +public class ConfigureAssetSetPage extends WizardPage implements SelectionListener, + ModifyListener { + private final CreateAssetSetWizardState mValues; + + private static final int PREVIEW_AREA_WIDTH = 144; + + private boolean mShown; + + private Composite mConfigurationArea; + private Button mImageRadio; + private Button mClipartRadio; + private Button mTextRadio; + private Button mPickImageButton; + private Button mTrimCheckBox; + private Slider mPaddingSlider; + private Label mPercentLabel; + private Button mCropRadio; + private Button mCenterRadio; + private Button mNoShapeRadio; + private Button mSquareRadio; + private Button mCircleButton; + private Button mBgButton; + private Button mFgButton; + private Composite mPreviewArea; + private Button mFontButton; + private Composite mForegroundArea; + private Composite mImageForm; + private Composite mClipartForm; + private Composite mTextForm; + private Text mImagePathText; + + private boolean mTimerPending; + private RGB mBgColor; + private RGB mFgColor; + private Text mText; + + /** Most recently set image path: preserved across wizard sessions */ + private static String sImagePath; + private Button mChooseClipart; + private Composite mClipartPreviewPanel; + private Label mThemeLabel; + private Composite mThemeComposite; + private Button mHoloLightRadio; + private Button mHoloDarkRadio; + private Label mScalingLabel; + private Composite mScalingComposite; + private Label mShapeLabel; + private Composite mShapeComposite; + private Label mBgColorLabel; + private Label mFgColorLabel; + + private boolean mIgnore; + private SourceType mShowingType; + + /** + * Create the wizard. + * + * @param values the wizard state + */ + public ConfigureAssetSetPage(CreateAssetSetWizardState values) { + super("configureAssetPage"); + mValues = values; + + setTitle("Configure Icon Set"); + setDescription("Configure the attributes of the icon set"); + } + + /** + * Create contents of the wizard. + * + * @param parent the parent widget + */ + @Override + @SuppressWarnings("unused") // Don't warn about unassigned "new Label(.)": has side-effect + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NULL); + + setControl(container); + GridLayout glContainer = new GridLayout(2, false); + glContainer.marginWidth = 0; + glContainer.horizontalSpacing = 0; + glContainer.marginHeight = 0; + glContainer.verticalSpacing = 0; + container.setLayout(glContainer); + + ScrolledComposite configurationScrollArea = new ScrolledComposite(container, SWT.V_SCROLL); + configurationScrollArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2)); + configurationScrollArea.setExpandHorizontal(true); + configurationScrollArea.setExpandVertical(true); + + mConfigurationArea = new Composite(configurationScrollArea, SWT.NONE); + GridLayout glConfigurationArea = new GridLayout(3, false); + glConfigurationArea.horizontalSpacing = 0; + glConfigurationArea.marginRight = 15; + glConfigurationArea.marginWidth = 0; + glConfigurationArea.marginHeight = 0; + mConfigurationArea.setLayout(glConfigurationArea); + + Label foregroundLabel = new Label(mConfigurationArea, SWT.NONE); + foregroundLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + foregroundLabel.setText("Foreground:"); + + Composite foregroundComposite = new Composite(mConfigurationArea, SWT.NONE); + foregroundComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + GridLayout glForegroundComposite = new GridLayout(5, false); + glForegroundComposite.horizontalSpacing = 0; + foregroundComposite.setLayout(glForegroundComposite); + + mImageRadio = new Button(foregroundComposite, SWT.FLAT | SWT.TOGGLE); + mImageRadio.setSelection(false); + mImageRadio.addSelectionListener(this); + mImageRadio.setText("Image"); + + mClipartRadio = new Button(foregroundComposite, SWT.FLAT | SWT.TOGGLE); + mClipartRadio.setText("Clipart"); + mClipartRadio.addSelectionListener(this); + + mTextRadio = new Button(foregroundComposite, SWT.FLAT | SWT.TOGGLE); + mTextRadio.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); + mTextRadio.setText("Text"); + mTextRadio.addSelectionListener(this); + new Label(mConfigurationArea, SWT.NONE); + + mForegroundArea = new Composite(mConfigurationArea, SWT.NONE); + mForegroundArea.setLayout(new StackLayout()); + mForegroundArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); + + mImageForm = new Composite(mForegroundArea, SWT.NONE); + mImageForm.setLayout(new GridLayout(3, false)); + + Label fileLabel = new Label(mImageForm, SWT.NONE); + fileLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + fileLabel.setText("Image File:"); + + mImagePathText = new Text(mImageForm, SWT.BORDER); + GridData pathLayoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); + pathLayoutData.widthHint = 200; + mImagePathText.setLayoutData(pathLayoutData); + mImagePathText.addSelectionListener(this); + mImagePathText.addModifyListener(this); + + mPickImageButton = new Button(mImageForm, SWT.FLAT); + mPickImageButton.setText("Browse..."); + mPickImageButton.addSelectionListener(this); + + mClipartForm = new Composite(mForegroundArea, SWT.NONE); + mClipartForm.setLayout(new GridLayout(2, false)); + + mChooseClipart = new Button(mClipartForm, SWT.FLAT); + mChooseClipart.setText("Choose..."); + mChooseClipart.addSelectionListener(this); + + mClipartPreviewPanel = new Composite(mClipartForm, SWT.NONE); + RowLayout rlClipartPreviewPanel = new RowLayout(SWT.HORIZONTAL); + rlClipartPreviewPanel.marginBottom = 0; + rlClipartPreviewPanel.marginTop = 0; + rlClipartPreviewPanel.center = true; + mClipartPreviewPanel.setLayout(rlClipartPreviewPanel); + mClipartPreviewPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + mTextForm = new Composite(mForegroundArea, SWT.NONE); + mTextForm.setLayout(new GridLayout(2, false)); + + Label textLabel = new Label(mTextForm, SWT.NONE); + textLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + textLabel.setText("Text:"); + + mText = new Text(mTextForm, SWT.BORDER); + mText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mText.addModifyListener(this); + + Label fontLabel = new Label(mTextForm, SWT.NONE); + fontLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + fontLabel.setText("Font:"); + + mFontButton = new Button(mTextForm, SWT.FLAT); + mFontButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1)); + mFontButton.addSelectionListener(this); + mFontButton.setText("Choose Font..."); + new Label(mConfigurationArea, SWT.NONE); + + mTrimCheckBox = new Button(mConfigurationArea, SWT.CHECK); + mTrimCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + mTrimCheckBox.setSelection(false); + mTrimCheckBox.setText("Trim Surrounding Blank Space"); + mTrimCheckBox.addSelectionListener(this); + new Label(mConfigurationArea, SWT.NONE); + + Label paddingLabel = new Label(mConfigurationArea, SWT.NONE); + paddingLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + paddingLabel.setText("Additional Padding:"); + new Label(mConfigurationArea, SWT.NONE); + + mPaddingSlider = new Slider(mConfigurationArea, SWT.NONE); + mPaddingSlider.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + // This doesn't work right -- not sure why. For now just use a plain slider + // and subtract 10 from it to get the real range. + //mPaddingSlider.setValues(0, -10, 50, 0, 1, 10); + //mPaddingSlider.setSelection(10 + 15); + mPaddingSlider.addSelectionListener(this); + + mPercentLabel = new Label(mConfigurationArea, SWT.NONE); + mPercentLabel.setText(" 15%"); // Enough available space for -10% + mScalingLabel = new Label(mConfigurationArea, SWT.NONE); + mScalingLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mScalingLabel.setText("Foreground Scaling:"); + + mScalingComposite = new Composite(mConfigurationArea, SWT.NONE); + mScalingComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + GridLayout gl_mScalingComposite = new GridLayout(5, false); + gl_mScalingComposite.horizontalSpacing = 0; + mScalingComposite.setLayout(gl_mScalingComposite); + + mCropRadio = new Button(mScalingComposite, SWT.FLAT | SWT.TOGGLE); + mCropRadio.setSelection(true); + mCropRadio.setText("Crop"); + mCropRadio.addSelectionListener(this); + + mCenterRadio = new Button(mScalingComposite, SWT.FLAT | SWT.TOGGLE); + mCenterRadio.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1)); + mCenterRadio.setText("Center"); + mCenterRadio.addSelectionListener(this); + + mShapeLabel = new Label(mConfigurationArea, SWT.NONE); + mShapeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mShapeLabel.setText("Shape"); + + mShapeComposite = new Composite(mConfigurationArea, SWT.NONE); + mShapeComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + GridLayout gl_mShapeComposite = new GridLayout(6, false); + gl_mShapeComposite.horizontalSpacing = 0; + mShapeComposite.setLayout(gl_mShapeComposite); + + mNoShapeRadio = new Button(mShapeComposite, SWT.FLAT | SWT.TOGGLE); + mNoShapeRadio.setText("None"); + mNoShapeRadio.addSelectionListener(this); + + mSquareRadio = new Button(mShapeComposite, SWT.FLAT | SWT.TOGGLE); + mSquareRadio.setSelection(true); + mSquareRadio.setText("Square"); + mSquareRadio.addSelectionListener(this); + + mCircleButton = new Button(mShapeComposite, SWT.FLAT | SWT.TOGGLE); + mCircleButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1)); + mCircleButton.setText("Circle"); + mCircleButton.addSelectionListener(this); + + mThemeLabel = new Label(mConfigurationArea, SWT.NONE); + mThemeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mThemeLabel.setText("Theme"); + + mThemeComposite = new Composite(mConfigurationArea, SWT.NONE); + mThemeComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + GridLayout gl_mThemeComposite = new GridLayout(2, false); + gl_mThemeComposite.horizontalSpacing = 0; + mThemeComposite.setLayout(gl_mThemeComposite); + + mHoloLightRadio = new Button(mThemeComposite, SWT.FLAT | SWT.TOGGLE); + mHoloLightRadio.setText("Holo Light"); + mHoloLightRadio.setSelection(true); + mHoloLightRadio.addSelectionListener(this); + + mHoloDarkRadio = new Button(mThemeComposite, SWT.FLAT | SWT.TOGGLE); + mHoloDarkRadio.setText("Holo Dark"); + mHoloDarkRadio.addSelectionListener(this); + + mBgColorLabel = new Label(mConfigurationArea, SWT.NONE); + mBgColorLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mBgColorLabel.setText("Background Color:"); + + mBgButton = new Button(mConfigurationArea, SWT.FLAT); + mBgButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + mBgButton.addSelectionListener(this); + mBgButton.setAlignment(SWT.CENTER); + + mFgColorLabel = new Label(mConfigurationArea, SWT.NONE); + mFgColorLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mFgColorLabel.setText("Foreground Color:"); + + mFgButton = new Button(mConfigurationArea, SWT.FLAT); + mFgButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + mFgButton.setAlignment(SWT.CENTER); + mFgButton.addSelectionListener(this); + + configurationScrollArea.setContent(mConfigurationArea); + configurationScrollArea.setMinSize(mConfigurationArea.computeSize(SWT.DEFAULT, + SWT.DEFAULT)); + + Label previewLabel = new Label(container, SWT.NONE); + previewLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1)); + previewLabel.setText("Preview:"); + + mPreviewArea = new Composite(container, SWT.BORDER); + + RowLayout rlPreviewAreaPreviewArea = new RowLayout(SWT.HORIZONTAL); + rlPreviewAreaPreviewArea.wrap = true; + rlPreviewAreaPreviewArea.pack = true; + rlPreviewAreaPreviewArea.center = true; + rlPreviewAreaPreviewArea.spacing = 0; + rlPreviewAreaPreviewArea.marginBottom = 0; + rlPreviewAreaPreviewArea.marginTop = 0; + rlPreviewAreaPreviewArea.marginRight = 0; + rlPreviewAreaPreviewArea.marginLeft = 0; + mPreviewArea.setLayout(rlPreviewAreaPreviewArea); + GridData gdMPreviewArea = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1); + gdMPreviewArea.widthHint = PREVIEW_AREA_WIDTH; + mPreviewArea.setLayoutData(gdMPreviewArea); + + // Initial color + Display display = parent.getDisplay(); + updateColor(display, mValues.background, true /*background*/); + updateColor(display, mValues.foreground, false /*background*/); + + setSourceType(mValues.sourceType); + + new Label(mConfigurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); + + validatePage(); + } + + void configureAssetType(AssetType type) { + if (mValues.sourceType != mShowingType) { + mShowingType = mValues.sourceType; + showGroup(type.needsForegroundScaling(), mScalingLabel, mScalingComposite); + showGroup(type.needsShape(), mShapeLabel, mShapeComposite); + showGroup(type.needsTheme(), mThemeLabel, mThemeComposite); + showGroup(type.needsColors(), mBgColorLabel, mBgButton); + showGroup(type.needsColors() && mValues.sourceType != SourceType.IMAGE, + mFgColorLabel, mFgButton); + + Composite parent = mScalingLabel.getParent(); + parent.pack(); + parent.layout(); + } + } + + private static void showGroup(boolean show, Control control1, Control control2) { + showControl(show, control1); + showControl(show, control2); + } + + private static void showControl(boolean show, Control control) { + Object data = control.getLayoutData(); + if (data instanceof GridData) { + GridData gridData = (GridData) data; + gridData.exclude = !show; + } + control.setVisible(show); + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + + // We update the image selection here rather than in {@link #createControl} because + // that method is called when the wizard is created, and we want to wait until the + // user has chosen a project before attempting to look up the right default image to use + if (visible) { + mShown = true; + + // Clear out old previews - important if the user goes back to page one, changes + // asset type and steps into page 2 - at that point we arrive here and we might + // display the old previews for a brief period until the preview delay timer expires. + for (Control c : mPreviewArea.getChildren()) { + c.dispose(); + } + mPreviewArea.layout(true); + + // Update asset type configuration: will show/hide parameter controls depending + // on which asset type is chosen + AssetType type = mValues.type; + assert type != null; + configureAssetType(type); + + // Initial image - use the most recently used image, or the default launcher + // icon created in our default projects, if there + if (mValues.imagePath != null) { + sImagePath = mValues.imagePath.getPath(); + } + if (sImagePath == null) { + IProject project = mValues.project; + if (project != null) { + IResource icon = project.findMember("res/drawable-hdpi/icon.png"); //$NON-NLS-1$ + if (icon != null) { + IWorkspaceRoot workspace = ResourcesPlugin.getWorkspace().getRoot(); + IPath workspacePath = workspace.getLocation(); + sImagePath = workspacePath.append(icon.getFullPath()).toOSString(); + } + } + } + if (sImagePath != null) { + mValues.imagePath = new File(sImagePath); + mImagePathText.setText(sImagePath); + } + + try { + mIgnore = true; + + mTrimCheckBox.setSelection(mValues.trim); + + // This doesn't work right -- not sure why. For now just use a plain slider + // and subtract 10 from it to get the real range. + //mPaddingSlider.setValues(0, -10, 50, 0, 1, 10); + //mPaddingSlider.setSelection(10 + 15); + mPaddingSlider.setSelection(mValues.padding + 10); + mPercentLabel.setText(Integer.toString(mValues.padding) + '%'); + + if (mValues.imagePath != null) { + mImagePathText.setText(mValues.imagePath.getPath()); + } + + if (mValues.text != null) { + mText.setText(mValues.text); + } + + setSourceType(mValues.sourceType); + + // Shape=NONE does not apply for notification icons; it's needed for API < 9 + if (mValues.shape == Shape.NONE && mValues.type == AssetType.NOTIFICATION) { + mValues.shape = Shape.SQUARE; + } + + setShape(mValues.shape); + mNoShapeRadio.setEnabled(mValues.type != AssetType.NOTIFICATION); + + if (mValues.sourceType == SourceType.CLIPART + && mValues.clipartName != null) { + updateClipartPreview(); + } + + // Initial color + Display display = mPreviewArea.getDisplay(); + //updateColor(display, new RGB(0xa4, 0xc6, 0x39), true /*background*/); + updateColor(display, mValues.background, true /*background*/); + updateColor(display, mValues.foreground, false /*background*/); + + updateTrimOptions(); + } finally { + mIgnore = false; + } + + validatePage(); + + requestUpdatePreview(true /*quickly*/); + + if (mTextRadio.getSelection()) { + mText.setFocus(); + } + } + } + + private void setSourceType(CreateAssetSetWizardState.SourceType sourceType) { + if (sourceType == CreateAssetSetWizardState.SourceType.IMAGE) { + chooseForegroundTab(mImageRadio, mImageForm); + } else if (sourceType == CreateAssetSetWizardState.SourceType.CLIPART) { + chooseForegroundTab(mClipartRadio, mClipartForm); + mChooseClipart.setFocus(); + } else if (sourceType == CreateAssetSetWizardState.SourceType.TEXT) { + updateFontLabel(); + chooseForegroundTab(mTextRadio, mTextForm); + mText.setFocus(); + } + } + + private void updateTrimOptions() { + // Trimming and padding is not available for clipart images; padding etc is + // predefined to work well with action bar icons + if (mValues.sourceType == SourceType.CLIPART + && mValues.type == AssetType.ACTIONBAR) { + mTrimCheckBox.setEnabled(false); + mPaddingSlider.setEnabled(false); + mValues.trim = false; + } else if (!mTrimCheckBox.isEnabled()) { + mTrimCheckBox.setEnabled(true); + mPaddingSlider.setEnabled(true); + } + } + + private boolean validatePage() { + String error = null; + //String warning = null; + + if (mImageRadio.getSelection()) { + String path = mValues.imagePath != null ? mValues.imagePath.getPath() : null; + if (path == null || path.length() == 0) { + error = "Select an image"; + } else if (path.equals(DEFAULT_LAUNCHER_ICON)) { + // Silent + } else if (!(new File(path).exists())) { + error = String.format("%1$s does not exist", path); + } else { + // Preserve across wizard sessions + sImagePath = path; + } + } else if (mTextRadio.getSelection()) { + if (mValues.text.length() == 0) { + error = "Enter text"; + } + } else { + assert mClipartRadio.getSelection(); + if (mValues.clipartName == null) { + error = "Select clip art"; + } + } + + setPageComplete(error == null); + if (error != null) { + setMessage(error, IMessageProvider.ERROR); + //} else if (warning != null) { + // setMessage(warning, IMessageProvider.WARNING); + } else { + setErrorMessage(null); + setMessage(null); + } + + return error == null; + } + + @Override + public boolean isPageComplete() { + // Force user to reach second page before hitting Finish + return mShown; + } + + // ---- Implements ModifyListener ---- + + @Override + public void modifyText(ModifyEvent e) { + if (mIgnore) { + return; + } + + if (e.getSource() == mImagePathText) { + mValues.imagePath = new File(mImagePathText.getText().trim()); + requestUpdatePreview(false); + } else if (e.getSource() == mText) { + mValues.text = mText.getText().trim(); + requestUpdatePreview(false); + } + + validatePage(); + } + + // ---- Implements SelectionListener ---- + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // Nothing to do + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (mIgnore) { + return; + } + + Object source = e.getSource(); + boolean updateQuickly = true; + + // Tabs + if (source == mImageRadio) { + mValues.sourceType = CreateAssetSetWizardState.SourceType.IMAGE; + chooseForegroundTab((Button) source, mImageForm); + configureAssetType(mValues.type); + updateTrimOptions(); + } else if (source == mClipartRadio) { + mValues.sourceType = CreateAssetSetWizardState.SourceType.CLIPART; + chooseForegroundTab((Button) source, mClipartForm); + configureAssetType(mValues.type); + updateTrimOptions(); + } else if (source == mTextRadio) { + mValues.sourceType = CreateAssetSetWizardState.SourceType.TEXT; + updateFontLabel(); + chooseForegroundTab((Button) source, mTextForm); + configureAssetType(mValues.type); + mText.setFocus(); + updateTrimOptions(); + } + + // Choose image file + if (source == mPickImageButton) { + FileDialog dialog = new FileDialog(mPickImageButton.getShell(), SWT.OPEN); + + String curLocation = mImagePathText.getText().trim(); + if (!curLocation.isEmpty()) { + dialog.setFilterPath(curLocation); + } + + String file = dialog.open(); + if (file != null) { + mValues.imagePath = new File(file); + mImagePathText.setText(file); + } + } + + // Enforce Radio Groups + if (source == mCropRadio) { + mCropRadio.setSelection(true); // Ensure that you can't toggle it off + mCenterRadio.setSelection(false); + mValues.crop = true; + } else if (source == mCenterRadio) { + mCenterRadio.setSelection(true); + mCropRadio.setSelection(false); + mValues.crop = false; + } + if (source == mSquareRadio) { + mValues.shape = GraphicGenerator.Shape.SQUARE; + setShape(mValues.shape); + } else if (source == mCircleButton) { + mValues.shape = GraphicGenerator.Shape.CIRCLE; + setShape(mValues.shape); + } else if (source == mNoShapeRadio) { + mValues.shape = GraphicGenerator.Shape.NONE; + setShape(mValues.shape); + } + + if (source == mTrimCheckBox) { + mValues.trim = mTrimCheckBox.getSelection(); + } + + if (source == mHoloDarkRadio) { + mHoloDarkRadio.setSelection(true); + mHoloLightRadio.setSelection(false); + mValues.holoDark = true; + } else if (source == mHoloLightRadio) { + mHoloLightRadio.setSelection(true); + mHoloDarkRadio.setSelection(false); + mValues.holoDark = false; + } + + if (source == mChooseClipart) { + MessageDialog dialog = new MessageDialog(mChooseClipart.getShell(), + "Choose Clip Art", + null, "Choose Clip Art Image:", MessageDialog.NONE, + new String[] { "Close" }, 0) { + @Override + protected Control createCustomArea(Composite parent) { + // Outer form which just establishes a width for the inner form which + // wraps in a RowLayout + Composite outer = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(); + outer.setLayout(gridLayout); + + Composite chooserForm = new Composite(outer, SWT.NONE); + GridData gd = new GridData(); + gd.grabExcessVerticalSpace = true; + gd.widthHint = 450; + chooserForm.setLayoutData(gd); + RowLayout clipartFormLayout = new RowLayout(SWT.HORIZONTAL); + clipartFormLayout.center = true; + clipartFormLayout.wrap = true; + chooserForm.setLayout(clipartFormLayout); + + MouseAdapter clickListener = new MouseAdapter() { + @Override + public void mouseDown(MouseEvent event) { + // Clicked on some of the sample art + if (event.widget instanceof ImageControl) { + ImageControl image = (ImageControl) event.widget; + mValues.clipartName = (String) image.getData(); + close(); + + updateClipartPreview(); + updatePreview(); + } + } + }; + Display display = chooserForm.getDisplay(); + Color hoverColor = display.getSystemColor(SWT.COLOR_RED); + Iterator<String> clipartImages = GraphicGenerator.getClipartNames(); + while (clipartImages.hasNext()) { + String name = clipartImages.next(); + try { + BufferedImage icon = GraphicGenerator.getClipartIcon(name); + if (icon != null) { + Image swtImage = SwtUtils.convertToSwt(display, icon, true, -1); + ImageControl img = new ImageControl(chooserForm, + SWT.NONE, swtImage); + img.setData(name); + img.setHoverColor(hoverColor); + img.addMouseListener(clickListener); + } + } catch (IOException e1) { + AdtPlugin.log(e1, null); + } + } + outer.pack(); + outer.layout(); + return outer; + } + }; + dialog.open(); + } + + if (source == mBgButton) { + ColorDialog dlg = new ColorDialog(mBgButton.getShell()); + dlg.setRGB(mBgColor); + dlg.setText("Choose a new Background Color"); + RGB rgb = dlg.open(); + if (rgb != null) { + // Dispose the old color, create the + // new one, and set into the label + mValues.background = rgb; + updateColor(mBgButton.getDisplay(), rgb, true /*background*/); + } + } else if (source == mFgButton) { + ColorDialog dlg = new ColorDialog(mFgButton.getShell()); + dlg.setRGB(mFgColor); + dlg.setText("Choose a new Foreground Color"); + RGB rgb = dlg.open(); + if (rgb != null) { + // Dispose the old color, create the + // new one, and set into the label + mValues.foreground = rgb; + updateColor(mFgButton.getDisplay(), rgb, false /*background*/); + } + } + + if (source == mFontButton) { + FontDialog dialog = new FontDialog(mFontButton.getShell()); + FontData[] fontList; + if (mFontButton.getData() == null) { + fontList = mFontButton.getDisplay().getFontList( + mValues.getTextFont().getFontName(), true /*scalable*/); + } else { + fontList = mFontButton.getFont().getFontData(); + } + dialog.setFontList(fontList); + FontData data = dialog.open(); + if (data != null) { + Font font = new Font(mFontButton.getDisplay(), dialog.getFontList()); + mFontButton.setFont(font); + mFontButton.setData(font); + + // Always use a large font for the rendering, even though user is typically + // picking small font sizes in the font chooser + //int dpi = mFontButton.getDisplay().getDPI().y; + //int height = (int) Math.round(fontData.getHeight() * dpi / 72.0); + int fontHeight = new TextRenderUtil.Options().fontSize; + FontData fontData = font.getFontData()[0]; + int awtStyle = java.awt.Font.PLAIN; + int swtStyle = fontData.getStyle(); + if ((swtStyle & SWT.ITALIC) != 0) { + awtStyle |= java.awt.Font.ITALIC; + } + if ((swtStyle & SWT.BOLD) != 0) { + awtStyle = java.awt.Font.BOLD; + } + mValues.setTextFont(new java.awt.Font(fontData.getName(), awtStyle, fontHeight)); + + updateFontLabel(); + mFontButton.getParent().pack(); + } + } + + if (source == mPaddingSlider) { + mValues.padding = getPadding(); + mPercentLabel.setText(Integer.toString(getPadding()) + '%'); + + // When dragging the slider, only do periodic updates + updateQuickly = false; + } + + requestUpdatePreview(updateQuickly); + } + + @SuppressWarnings("unused") // SWT constructors have side effects and are not unused + private void updateClipartPreview() { + for (Control c : mClipartPreviewPanel.getChildren()) { + c.dispose(); + } + if (mClipartPreviewPanel.getChildren().length == 0) { + try { + BufferedImage icon = GraphicGenerator.getClipartIcon( + mValues.clipartName); + if (icon != null) { + Display display = mClipartForm.getDisplay(); + Image swtImage = SwtUtils.convertToSwt(display, icon, + true, -1); + new ImageControl(mClipartPreviewPanel, + SWT.NONE, swtImage); + } + } catch (IOException e1) { + AdtPlugin.log(e1, null); + } + mClipartPreviewPanel.pack(); + mClipartPreviewPanel.layout(); + } + } + + private void setShape(GraphicGenerator.Shape shape) { + if (shape == GraphicGenerator.Shape.SQUARE) { + mSquareRadio.setSelection(true); + mCircleButton.setSelection(false); + mNoShapeRadio.setSelection(false); + } else if (shape == GraphicGenerator.Shape.CIRCLE) { + mCircleButton.setSelection(true); + mSquareRadio.setSelection(false); + mNoShapeRadio.setSelection(false); + } else if (shape == GraphicGenerator.Shape.NONE) { + mNoShapeRadio.setSelection(true); + mCircleButton.setSelection(false); + mSquareRadio.setSelection(false); + } else { + assert false : shape; + } + } + + private void updateFontLabel() { + mFontButton.setText(mValues.getTextFont().getFontName()); + } + + private int getPadding() { + // Shifted - see comment for mPaddingSlider construction for an explanation + return mPaddingSlider.getSelection() - 10; + } + + private void chooseForegroundTab(Button newButton, Composite newArea) { + if (newButton.getSelection()) { + mImageRadio.setSelection(false); + mClipartRadio.setSelection(false); + mTextRadio.setSelection(false); + newButton.setSelection(true); + StackLayout stackLayout = (StackLayout) mForegroundArea.getLayout(); + stackLayout.topControl = newArea; + mForegroundArea.layout(); + } else { + // Treat it as a radio button: you can't click to turn it off, you have to + // click on one of the other buttons + newButton.setSelection(true); + } + } + + /** + * Delay updates of the preview, to ensure that the SWT UI acts immediately (to handle + * radio group selections etc). + * + * @param quickly if true, update the previews soon, otherwise schedule one a bit later + */ + private void requestUpdatePreview(boolean quickly) { + if (mTimerPending) { + return; + } + mTimerPending = true; + + final Runnable timer = new Runnable() { + @Override + public void run() { + mTimerPending = false; + updatePreview(); + } + }; + + mPreviewArea.getDisplay().timerExec(quickly ? 10 : 250, timer); + } + + private void updatePreview() { + Display display = mPreviewArea.getDisplay(); + + for (Control c : mPreviewArea.getChildren()) { + c.dispose(); + } + + if (!validatePage()) { + return; + } + + Map<String, Map<String, BufferedImage>> map = generateImages(mValues, + true /*previewOnly*/, this); + for (Entry<String, Map<String, BufferedImage>> categoryEntry : map.entrySet()) { + String category = categoryEntry.getKey(); + if (category.length() > 0) { + Label nameLabel = new Label(mPreviewArea, SWT.NONE); + nameLabel.setText(String.format("%1$s:", category)); + RowData rowData = new RowData(); + nameLabel.setLayoutData(rowData); + // Ensure these get their own rows + rowData.width = PREVIEW_AREA_WIDTH; + } + + Map<String, BufferedImage> images = categoryEntry.getValue(); + for (Entry<String, BufferedImage> entry : images.entrySet()) { + BufferedImage image = entry.getValue(); + Image swtImage = SwtUtils.convertToSwt(display, image, true, -1); + if (swtImage != null) { + @SuppressWarnings("unused") // Has side effect + ImageControl imageControl = new ImageControl(mPreviewArea, SWT.NONE, swtImage); + } + } + } + + mPreviewArea.layout(true); + } + + /** + * Generate images using the given wizard state + * + * @param mValues the state to use + * @param previewOnly whether we are only generating previews + * @param page if non null, a wizard page to write error messages to + * @return a map of image objects + */ + public static Map<String, Map<String, BufferedImage>> generateImages( + @NonNull CreateAssetSetWizardState mValues, + boolean previewOnly, + @Nullable WizardPage page) { + // Map of ids to images: Preserve insertion order (the densities) + Map<String, Map<String, BufferedImage>> categoryMap = + new LinkedHashMap<String, Map<String, BufferedImage>>(); + + AssetType type = mValues.type; + boolean trim = mValues.trim; + + BufferedImage sourceImage = null; + switch (mValues.sourceType) { + case IMAGE: { + // Load the image + // TODO: Only do this when the source image type is image + String path = mValues.imagePath != null ? mValues.imagePath.getPath() : ""; + if (path.length() == 0) { + if (page != null) { + page.setErrorMessage("Enter a filename"); + } + return Collections.emptyMap(); + } + if (!path.equals(DEFAULT_LAUNCHER_ICON)) { + File file = new File(path); + if (!file.isFile()) { + if (page != null) { + page.setErrorMessage(String.format("%1$s does not exist", file.getPath())); + } + return Collections.emptyMap(); + } + } + + if (page != null) { + page.setErrorMessage(null); + } + try { + sourceImage = mValues.getCachedImage(path, false); + if (sourceImage != null) { + if (trim) { + sourceImage = ImageUtils.cropBlank(sourceImage, null, TYPE_INT_ARGB); + } + if (mValues.padding != 0) { + sourceImage = Util.paddedImage(sourceImage, mValues.padding); + } + } + } catch (IOException ioe) { + if (page != null) { + page.setErrorMessage(ioe.getLocalizedMessage()); + } + } + break; + } + case CLIPART: { + try { + sourceImage = GraphicGenerator.getClipartImage(mValues.clipartName); + + boolean isActionBar = mValues.type == AssetType.ACTIONBAR; + if (trim && !isActionBar) { + sourceImage = ImageUtils.cropBlank(sourceImage, null, TYPE_INT_ARGB); + } + + if (type.needsColors()) { + RGB fg = mValues.foreground; + int color = 0xFF000000 | (fg.red << 16) | (fg.green << 8) | fg.blue; + Paint paint = new java.awt.Color(color); + sourceImage = Util.filledImage(sourceImage, paint); + } + + int padding = mValues.padding; + if (padding != 0 && !isActionBar) { + sourceImage = Util.paddedImage(sourceImage, padding); + } + } catch (IOException e) { + AdtPlugin.log(e, null); + return categoryMap; + } + break; + } + case TEXT: { + String text = mValues.text; + TextRenderUtil.Options options = new TextRenderUtil.Options(); + options.font = mValues.getTextFont(); + int color; + if (type.needsColors()) { + RGB fg = mValues.foreground; + color = 0xFF000000 | (fg.red << 16) | (fg.green << 8) | fg.blue; + } else { + color = 0xFFFFFFFF; + } + options.foregroundColor = color; + sourceImage = TextRenderUtil.renderTextImage(text, mValues.padding, options); + + if (trim) { + sourceImage = ImageUtils.cropBlank(sourceImage, null, TYPE_INT_ARGB); + } + + int padding = mValues.padding; + if (padding != 0) { + sourceImage = Util.paddedImage(sourceImage, padding); + } + break; + } + } + + GraphicGenerator generator = null; + GraphicGenerator.Options options = null; + switch (type) { + case LAUNCHER: { + generator = new LauncherIconGenerator(); + LauncherIconGenerator.LauncherOptions launcherOptions = + new LauncherIconGenerator.LauncherOptions(); + launcherOptions.shape = mValues.shape; + launcherOptions.crop = mValues.crop; + launcherOptions.style = GraphicGenerator.Style.SIMPLE; + + RGB bg = mValues.background; + int color = (bg.red << 16) | (bg.green << 8) | bg.blue; + launcherOptions.backgroundColor = color; + // Flag which tells the generator iterator to include a web graphic + launcherOptions.isWebGraphic = !previewOnly; + options = launcherOptions; + + break; + } + case MENU: + generator = new MenuIconGenerator(); + options = new GraphicGenerator.Options(); + break; + case ACTIONBAR: { + generator = new ActionBarIconGenerator(); + ActionBarIconGenerator.ActionBarOptions actionBarOptions = + new ActionBarIconGenerator.ActionBarOptions(); + actionBarOptions.theme = mValues.holoDark + ? ActionBarIconGenerator.Theme.HOLO_DARK + : ActionBarIconGenerator.Theme.HOLO_LIGHT; + actionBarOptions.sourceIsClipart = (mValues.sourceType == SourceType.CLIPART); + + options = actionBarOptions; + break; + } + case NOTIFICATION: { + generator = new NotificationIconGenerator(); + options = new NotificationIconGenerator.NotificationOptions(); + break; + } + case TAB: + generator = new TabIconGenerator(); + options = new TabIconGenerator.TabOptions(); + break; + default: + AdtPlugin.log(IStatus.ERROR, "Unsupported asset type: %1$s", type); + return categoryMap; + } + + options.sourceImage = sourceImage; + + IProject project = mValues.project; + if (mValues.minSdk != -1) { + options.minSdk = mValues.minSdk; + } else { + Pair<Integer, Integer> v = ManifestInfo.computeSdkVersions(project); + options.minSdk = v.getFirst(); + } + + String baseName = mValues.outputName; + generator.generate(null, categoryMap, mValues, options, baseName); + + return categoryMap; + } + + /** + * Generate custom icons into the project based on the asset studio wizard + * state + * + * @param newProject the project to write into + * @param values the wizard state to read configuration settings from + * @param previewOnly whether we are only generating a preview. For example, + * the launcher icons won't generate a huge 512x512 web graphic + * in preview mode + * @param page a wizard page to write error messages to, or null + */ + public static void generateIcons(final IProject newProject, + @NonNull CreateAssetSetWizardState values, + boolean previewOnly, + @Nullable WizardPage page) { + // Generate the custom icons + Map<String, Map<String, BufferedImage>> categories = generateImages(values, + false /*previewOnly*/, page); + for (Map<String, BufferedImage> previews : categories.values()) { + for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) { + String relativePath = entry.getKey(); + IPath dest = new Path(relativePath); + IFile file = newProject.getFile(dest); + + // In case template already created icons (should remove that) + // remove them first + if (file.exists()) { + try { + file.delete(true, new NullProgressMonitor()); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + } + AdtUtils.createWsParentDirectory(file.getParent()); + BufferedImage image = entry.getValue(); + + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "PNG", stream); //$NON-NLS-1$ + byte[] bytes = stream.toByteArray(); + InputStream is = new ByteArrayInputStream(bytes); + file.create(is, true /*force*/, null /*progress*/); + } catch (IOException e) { + AdtPlugin.log(e, null); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + + try { + file.getParent().refreshLocal(1, new NullProgressMonitor()); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } + } + } + } + + private void updateColor(Display display, RGB color, boolean isBackground) { + // Button.setBackgroundColor does not work (at least not on OSX) so + // we instead have to use Button.setImage with an image of the given + // color + BufferedImage coloredImage = ImageUtils.createColoredImage(60, 20, color); + Image image = SwtUtils.convertToSwt(display, coloredImage, false, -1); + + if (isBackground) { + mBgColor = color; + mBgButton.setImage(image); + } else { + mFgColor = color; + mFgButton.setImage(image); + } + } +} |