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.ui; 17 18 import static com.android.SdkConstants.DOT_9PNG; 19 import static com.android.utils.SdkUtils.endsWithIgnoreCase; 20 21 import com.android.ide.common.rendering.api.ResourceValue; 22 import com.android.ide.common.resources.ResourceResolver; 23 import com.android.ide.eclipse.adt.AdtPlugin; 24 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; 25 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; 26 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; 27 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService; 28 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils; 29 import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; 30 import com.android.resources.ResourceType; 31 32 import org.eclipse.core.runtime.IStatus; 33 import org.eclipse.jface.dialogs.DialogTray; 34 import org.eclipse.jface.dialogs.TrayDialog; 35 import org.eclipse.swt.SWT; 36 import org.eclipse.swt.graphics.RGB; 37 import org.eclipse.swt.layout.GridData; 38 import org.eclipse.swt.layout.GridLayout; 39 import org.eclipse.swt.widgets.Composite; 40 import org.eclipse.swt.widgets.Control; 41 import org.eclipse.swt.widgets.Display; 42 import org.eclipse.swt.widgets.Label; 43 44 import java.awt.image.BufferedImage; 45 import java.io.File; 46 import java.io.IOException; 47 48 import javax.imageio.ImageIO; 49 50 /** 51 * The {@link ResourcePreviewHelper} provides help to {@link TrayDialog} resource choosers 52 * where some resources (such as drawables and colors) are previewed in the tray area. 53 */ 54 public class ResourcePreviewHelper { 55 /** 56 * The width of the preview rendering 57 * <p> 58 * TODO: Make the preview rendering resize if the tray area is resized 59 */ 60 private static final int WIDTH = 100; 61 /** The height of the preview rendering */ 62 private static final int HEIGHT = 100; 63 64 private final GraphicalEditorPart mEditor; 65 private final TrayDialog mTrayDialog; 66 67 private boolean mShowingPreview; 68 private DialogTray mPreviewTray; 69 private ImageControl mPreviewImageControl; 70 71 /** 72 * Constructs a new {@link ResourcePreviewHelper}. 73 * <p> 74 * TODO: Add support for performing previews without an associated graphical editor, 75 * such as previewing icons from the manifest form editor; just pick default 76 * configuration settings in that case. 77 * 78 * @param trayDialog the associated tray-capable dialog 79 * @param editor a graphical editor. This is currently needed in order to provide 80 * configuration data for the rendering. 81 */ 82 public ResourcePreviewHelper(TrayDialog trayDialog, GraphicalEditorPart editor) { 83 this.mTrayDialog = trayDialog; 84 this.mEditor = editor; 85 } 86 87 /** 88 * Updates the preview based on the current selection and resource type, possibly 89 * hiding or opening the tray in the process. 90 * 91 * @param type the resource type for the selected resource 92 * @param resource the full resource url 93 */ 94 public void updatePreview(ResourceType type, String resource) { 95 boolean showPreview = type == ResourceType.DRAWABLE || type == ResourceType.COLOR; 96 if (showPreview) { 97 if (mPreviewTray == null) { 98 mPreviewTray = new DialogTray() { 99 @Override 100 protected Control createContents(Composite parent) { 101 // This creates a centered image control 102 Composite panel = new Composite(parent, SWT.NONE); 103 panel.setLayout(new GridLayout(3, false)); 104 Label dummy1 = new Label(panel, SWT.NONE); 105 dummy1.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true, 1, 1)); 106 mPreviewImageControl = new ImageControl(panel, SWT.NONE, SwtUtils 107 .createEmptyImage(parent.getDisplay(), WIDTH, HEIGHT)); 108 GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1); 109 gd.widthHint = WIDTH; 110 gd.heightHint = HEIGHT; 111 mPreviewImageControl.setLayoutData(gd); 112 Label dummy2 = new Label(panel, SWT.NONE); 113 dummy2.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, true, 1, 1)); 114 115 return panel; 116 } 117 118 }; 119 } 120 121 if (!mShowingPreview) { 122 mTrayDialog.openTray(mPreviewTray); 123 } 124 125 BufferedImage image = null; 126 try { 127 if (type == ResourceType.COLOR) { 128 ResourceResolver resources = mEditor.getResourceResolver(); 129 ResourceValue value = resources.findResValue(resource, false); 130 if (value != null) { 131 RGB color = ResourceHelper.resolveColor(resources, value); 132 if (color != null) { 133 image = ImageUtils.createColoredImage(WIDTH, HEIGHT, color); 134 } 135 } 136 } else { 137 assert type == ResourceType.DRAWABLE; 138 139 ResourceResolver resources = mEditor.getResourceResolver(); 140 ResourceValue drawable = resources.findResValue(resource, false); 141 if (drawable != null) { 142 String path = drawable.getValue(); 143 144 // Special-case image files (other than 9-patch files) and render these 145 // directly, in order to provide proper aspect ratio handling and 146 // to handle scaling to show the full contents: 147 if (ImageUtils.hasImageExtension(path) 148 && !endsWithIgnoreCase(path, DOT_9PNG)) { 149 File file = new File(path); 150 if (file.exists()) { 151 try { 152 image = ImageIO.read(file); 153 int width = image.getWidth(); 154 int height = image.getHeight(); 155 if (width > WIDTH || height > HEIGHT) { 156 double xScale = WIDTH / (double) width; 157 double yScale = HEIGHT / (double) height; 158 double scale = Math.min(xScale, yScale); 159 image = ImageUtils.scale(image, scale, scale); 160 } 161 } catch (IOException e) { 162 AdtPlugin.log(e, "Can't read preview image %1$s", path); 163 } 164 } 165 } 166 167 if (image == null) { 168 RenderService renderService = RenderService.create(mEditor); 169 renderService.setOverrideRenderSize(WIDTH, HEIGHT); 170 image = renderService.renderDrawable(drawable); 171 } 172 } 173 } 174 } catch (Throwable t) { 175 // Some kind of rendering error occurred. However, we don't want to use 176 // AdtPlugin.log(t, "Can't generate preview for %1$s", resource); 177 // because if it's a severe type of error (such as an InternalError shown 178 // in issue #18623) then a dialog will pop up and interfere with the 179 // preview, so just log a warning (unfortunately without the trace) instead. 180 AdtPlugin.log(IStatus.WARNING, "Can't generate preview for %1$s", resource); 181 } 182 183 Display display = mEditor.getSite().getShell().getDisplay(); 184 if (image != null) { 185 mPreviewImageControl.setImage(SwtUtils.convertToSwt(display, image, true, -1)); 186 } else { 187 mPreviewImageControl.setImage(SwtUtils.createEmptyImage(display, WIDTH, HEIGHT)); 188 } 189 mPreviewImageControl.redraw(); 190 } else if (mPreviewTray != null && mShowingPreview) { 191 mTrayDialog.closeTray(); 192 } 193 mShowingPreview = showPreview; 194 } 195 } 196