1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.ide.eclipse.adt.internal.assetstudio; 17 18 import com.android.ide.eclipse.adt.AdtConstants; 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.AdtUtils; 21 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; 22 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 23 import com.android.utils.Pair; 24 25 import org.eclipse.core.resources.IContainer; 26 import org.eclipse.core.resources.IFile; 27 import org.eclipse.core.resources.IProject; 28 import org.eclipse.core.resources.IResource; 29 import org.eclipse.core.runtime.CoreException; 30 import org.eclipse.core.runtime.IAdaptable; 31 import org.eclipse.core.runtime.IPath; 32 import org.eclipse.core.runtime.NullProgressMonitor; 33 import org.eclipse.core.runtime.Path; 34 import org.eclipse.jdt.core.IJavaProject; 35 import org.eclipse.jdt.ui.JavaUI; 36 import org.eclipse.jface.dialogs.MessageDialog; 37 import org.eclipse.jface.viewers.ISelectionProvider; 38 import org.eclipse.jface.viewers.IStructuredSelection; 39 import org.eclipse.jface.viewers.TreePath; 40 import org.eclipse.jface.viewers.TreeSelection; 41 import org.eclipse.jface.wizard.Wizard; 42 import org.eclipse.swt.SWT; 43 import org.eclipse.ui.IEditorPart; 44 import org.eclipse.ui.INewWizard; 45 import org.eclipse.ui.IViewPart; 46 import org.eclipse.ui.IWorkbench; 47 import org.eclipse.ui.IWorkbenchPage; 48 import org.eclipse.ui.IWorkbenchPartSite; 49 import org.eclipse.ui.IWorkbenchWindow; 50 import org.eclipse.ui.PlatformUI; 51 import org.eclipse.ui.part.FileEditorInput; 52 53 import java.awt.image.BufferedImage; 54 import java.io.ByteArrayInputStream; 55 import java.io.ByteArrayOutputStream; 56 import java.io.IOException; 57 import java.io.InputStream; 58 import java.util.ArrayList; 59 import java.util.Collections; 60 import java.util.List; 61 import java.util.Map; 62 63 import javax.imageio.ImageIO; 64 65 /** 66 * Wizard for creating a new icon set 67 */ 68 public class CreateAssetSetWizard extends Wizard implements INewWizard { 69 private ChooseAssetTypePage mChooseAssetPage; 70 private ConfigureAssetSetPage mConfigureAssetPage; 71 private IProject mInitialProject; 72 private List<IResource> mCreatedFiles; 73 private CreateAssetSetWizardState mValues = new CreateAssetSetWizardState(); 74 75 /** Creates a new asset set wizard */ 76 public CreateAssetSetWizard() { 77 setWindowTitle("Create Asset Set"); 78 } 79 80 @Override 81 public void addPages() { 82 mValues.project = mInitialProject; 83 84 mChooseAssetPage = new ChooseAssetTypePage(mValues); 85 mConfigureAssetPage = new ConfigureAssetSetPage(mValues); 86 87 addPage(mChooseAssetPage); 88 addPage(mConfigureAssetPage); 89 } 90 91 @Override 92 public boolean performFinish() { 93 Map<String, Map<String, BufferedImage>> categories = 94 ConfigureAssetSetPage.generateImages(mValues, false, null); 95 96 IProject project = mValues.project; 97 98 // Write out the images into the project 99 boolean yesToAll = false; 100 mCreatedFiles = new ArrayList<IResource>(); 101 102 for (Map<String, BufferedImage> previews : categories.values()) { 103 for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) { 104 String relativePath = entry.getKey(); 105 IPath dest = new Path(relativePath); 106 IFile file = project.getFile(dest); 107 if (file.exists()) { 108 // Warn that the file already exists and ask the user what to do 109 if (!yesToAll) { 110 MessageDialog dialog = new MessageDialog(null, "File Already Exists", null, 111 String.format( 112 "%1$s already exists.\nWould you like to replace it?", 113 file.getProjectRelativePath().toOSString()), 114 MessageDialog.QUESTION, new String[] { 115 // Yes will be moved to the end because it's the default 116 "Yes", "No", "Cancel", "Yes to All" 117 }, 0); 118 int result = dialog.open(); 119 switch (result) { 120 case 0: 121 // Yes 122 break; 123 case 3: 124 // Yes to all 125 yesToAll = true; 126 break; 127 case 1: 128 // No 129 continue; 130 case SWT.DEFAULT: 131 case 2: 132 // Cancel 133 return false; 134 } 135 } 136 137 try { 138 file.delete(true, new NullProgressMonitor()); 139 } catch (CoreException e) { 140 AdtPlugin.log(e, null); 141 } 142 } 143 144 AdtUtils.createWsParentDirectory(file.getParent()); 145 BufferedImage image = entry.getValue(); 146 147 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 148 try { 149 ImageIO.write(image, "PNG", stream); //$NON-NLS-1$ 150 byte[] bytes = stream.toByteArray(); 151 InputStream is = new ByteArrayInputStream(bytes); 152 file.create(is, true /*force*/, null /*progress*/); 153 mCreatedFiles.add(file); 154 } catch (IOException e) { 155 AdtPlugin.log(e, null); 156 } catch (CoreException e) { 157 AdtPlugin.log(e, null); 158 } 159 160 try { 161 file.getParent().refreshLocal(1, new NullProgressMonitor()); 162 } catch (CoreException e) { 163 AdtPlugin.log(e, null); 164 } 165 } 166 } 167 168 // Finally select the files themselves 169 selectFiles(project, mCreatedFiles); 170 171 return true; 172 } 173 174 private void selectFiles(IProject project, List<? extends IResource> createdFiles) { 175 // Attempt to select the newly created files in the Package Explorer 176 IWorkbench workbench = AdtPlugin.getDefault().getWorkbench(); 177 IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage(); 178 IViewPart viewPart = page.findView(JavaUI.ID_PACKAGES); 179 if (viewPart != null) { 180 IWorkbenchPartSite site = viewPart.getSite(); 181 IJavaProject javaProject = null; 182 try { 183 javaProject = BaseProjectHelper.getJavaProject(project); 184 } catch (CoreException e) { 185 AdtPlugin.log(e, null); 186 } 187 final ISelectionProvider provider = site.getSelectionProvider(); 188 if (provider != null) { 189 List<TreePath> pathList = new ArrayList<TreePath>(); 190 for (IResource file : createdFiles) { 191 // Create a TreePath for the given file, 192 // which should be the JavaProject, followed by the folders down to 193 // the final file. 194 List<Object> segments = new ArrayList<Object>(); 195 segments.add(file); 196 IContainer folder = file.getParent(); 197 if (folder != null && !(folder instanceof IProject)) { 198 segments.add(folder); 199 // res folder 200 folder = folder.getParent(); 201 if (folder != null && !(folder instanceof IProject)) { 202 segments.add(folder); 203 } 204 } 205 // project 206 segments.add(javaProject); 207 208 Collections.reverse(segments); 209 TreePath path = new TreePath(segments.toArray()); 210 pathList.add(path); 211 212 // IDEA: Maybe normalize the files backwards (IFile objects aren't unique) 213 // by maybe using the package explorer icons instead 214 } 215 216 TreePath[] paths = pathList.toArray(new TreePath[pathList.size()]); 217 final TreeSelection selection = new TreeSelection(paths); 218 219 provider.setSelection(selection); 220 221 // Workaround: The above doesn't always work; it will frequently select 222 // some siblings of the real files. I've tried a number of workarounds: 223 // normalizing the IFile objects by looking up the canonical ones via 224 // their relative paths from the project; deferring execution with 225 // Display.asyncRun; first calling select on the parents, etc. 226 // However, it turns out a simple workaround works best: Calling this 227 // method TWICE. The first call seems to expand all the necessary parents, 228 // and the second call ensures that the correct children are selected! 229 provider.setSelection(selection); 230 231 viewPart.setFocus(); 232 } 233 } 234 } 235 236 /** Sets the initial project to be used by the wizard */ 237 void setProject(IProject project) { 238 mInitialProject = project; 239 mValues.project = project; 240 } 241 242 @Override 243 public void init(IWorkbench workbench, IStructuredSelection selection) { 244 setHelpAvailable(false); 245 246 mInitialProject = guessProject(selection); 247 mValues.project = mInitialProject; 248 } 249 250 private IProject guessProject(IStructuredSelection selection) { 251 if (selection == null) { 252 return null; 253 } 254 255 for (Object element : selection.toList()) { 256 if (element instanceof IAdaptable) { 257 IResource res = (IResource) ((IAdaptable) element).getAdapter(IResource.class); 258 IProject project = res != null ? res.getProject() : null; 259 260 // Is this an Android project? 261 try { 262 if (project == null || !project.hasNature(AdtConstants.NATURE_DEFAULT)) { 263 continue; 264 } 265 } catch (CoreException e) { 266 // checking the nature failed, ignore this resource 267 continue; 268 } 269 270 return project; 271 } else if (element instanceof Pair<?, ?>) { 272 // Pair of Project/String 273 @SuppressWarnings("unchecked") 274 Pair<IProject, String> pair = (Pair<IProject, String>) element; 275 return pair.getFirst(); 276 } 277 } 278 279 // Try to figure out the project from the active editor 280 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); 281 if (window != null) { 282 IWorkbenchPage page = window.getActivePage(); 283 if (page != null) { 284 IEditorPart activeEditor = page.getActiveEditor(); 285 if (activeEditor instanceof AndroidXmlEditor) { 286 Object input = ((AndroidXmlEditor) activeEditor).getEditorInput(); 287 if (input instanceof FileEditorInput) { 288 FileEditorInput fileInput = (FileEditorInput) input; 289 return fileInput.getFile().getProject(); 290 } 291 } 292 } 293 } 294 295 IJavaProject[] projects = AdtUtils.getOpenAndroidProjects(); 296 if (projects != null && projects.length == 1) { 297 return projects[0].getProject(); 298 } 299 300 return null; 301 } 302 303 /** 304 * Returns the list of files created by the wizard. This method will return 305 * null if {@link #performFinish()} has not yet been called. 306 * 307 * @return a list of files created by the wizard, or null 308 */ 309 List<IResource> getCreatedFiles() { 310 return mCreatedFiles; 311 } 312 } 313