diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java | 1696 |
1 files changed, 0 insertions, 1696 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java deleted file mode 100644 index 98dde86e0..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderPreviewManager.java +++ /dev/null @@ -1,1696 +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.editors.layout.gle2; - -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.CFG_DEVICE_STATE; -import static com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration.MASK_ALL; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SHADOW_SIZE; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils.SMALL_SHADOW_SIZE; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreview.LARGE_SHADOWS; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.CUSTOM; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.NONE; -import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewMode.SCREENS; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.api.Rect; -import com.android.ide.common.rendering.api.Capability; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.ide.common.resources.configuration.DeviceConfigHelper; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.ide.common.resources.configuration.ScreenSizeQualifier; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.AdtUtils; -import com.android.ide.eclipse.adt.internal.editors.IconFactory; -import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationClient; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationDescription; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Locale; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.NestedConfiguration; -import com.android.ide.eclipse.adt.internal.editors.layout.configuration.VaryingConfiguration; -import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.resources.Density; -import com.android.resources.ScreenSize; -import com.android.sdklib.devices.Device; -import com.android.sdklib.devices.Screen; -import com.android.sdklib.devices.State; -import com.google.common.collect.Lists; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.dialogs.InputDialog; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.widgets.ScrollBar; -import org.eclipse.ui.IWorkbenchPartSite; -import org.eclipse.ui.PartInitException; -import org.eclipse.ui.ide.IDE; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -/** - * Manager for the configuration previews, which handles layout computations, - * managing the image buffer cache, etc - */ -public class RenderPreviewManager { - private static double sScale = 1.0; - private static final int RENDER_DELAY = 150; - private static final int PREVIEW_VGAP = 18; - private static final int PREVIEW_HGAP = 12; - private static final int MAX_WIDTH = 200; - private static final int MAX_HEIGHT = MAX_WIDTH; - private static final int ZOOM_ICON_WIDTH = 16; - private static final int ZOOM_ICON_HEIGHT = 16; - private @Nullable List<RenderPreview> mPreviews; - private @Nullable RenderPreviewList mManualList; - private final @NonNull LayoutCanvas mCanvas; - private final @NonNull CanvasTransform mVScale; - private final @NonNull CanvasTransform mHScale; - private int mPrevCanvasWidth; - private int mPrevCanvasHeight; - private int mPrevImageWidth; - private int mPrevImageHeight; - private @NonNull RenderPreviewMode mMode = NONE; - private @Nullable RenderPreview mActivePreview; - private @Nullable ScrollBarListener mListener; - private int mLayoutHeight; - /** Last seen state revision in this {@link RenderPreviewManager}. If less - * than {@link #sRevision}, the previews need to be updated on next exposure */ - private static int mRevision; - /** Current global revision count */ - private static int sRevision; - private boolean mNeedLayout; - private boolean mNeedRender; - private boolean mNeedZoom; - private SwapAnimation mAnimation; - - /** - * Creates a {@link RenderPreviewManager} associated with the given canvas - * - * @param canvas the canvas to manage previews for - */ - public RenderPreviewManager(@NonNull LayoutCanvas canvas) { - mCanvas = canvas; - mHScale = canvas.getHorizontalTransform(); - mVScale = canvas.getVerticalTransform(); - } - - /** - * Revise the global state revision counter. This will cause all layout - * preview managers to refresh themselves to the latest revision when they - * are next exposed. - */ - public static void bumpRevision() { - sRevision++; - } - - /** - * Returns the associated chooser - * - * @return the associated chooser - */ - @NonNull - ConfigurationChooser getChooser() { - GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor(); - return editor.getConfigurationChooser(); - } - - /** - * Returns the associated canvas - * - * @return the canvas - */ - @NonNull - public LayoutCanvas getCanvas() { - return mCanvas; - } - - /** Zooms in (grows all previews) */ - public void zoomIn() { - sScale = sScale * (1 / 0.9); - if (Math.abs(sScale-1.0) < 0.0001) { - sScale = 1.0; - } - - updatedZoom(); - } - - /** Zooms out (shrinks all previews) */ - public void zoomOut() { - sScale = sScale * (0.9 / 1); - if (Math.abs(sScale-1.0) < 0.0001) { - sScale = 1.0; - } - updatedZoom(); - } - - /** Zooms to 100 (resets zoom) */ - public void zoomReset() { - sScale = 1.0; - updatedZoom(); - mNeedZoom = mNeedLayout = true; - mCanvas.redraw(); - } - - private void updatedZoom() { - if (hasPreviews()) { - for (RenderPreview preview : mPreviews) { - preview.disposeThumbnail(); - } - RenderPreview preview = mCanvas.getPreview(); - if (preview != null) { - preview.disposeThumbnail(); - } - } - - mNeedLayout = mNeedRender = true; - mCanvas.redraw(); - } - - static int getMaxWidth() { - return (int) (sScale * MAX_WIDTH); - } - - static int getMaxHeight() { - return (int) (sScale * MAX_HEIGHT); - } - - static double getScale() { - return sScale; - } - - /** - * Returns whether there are any manual preview items (provided the current - * mode is manual previews - * - * @return true if there are items in the manual preview list - */ - public boolean hasManualPreviews() { - assert mMode == CUSTOM; - return mManualList != null && !mManualList.isEmpty(); - } - - /** Delete all the previews */ - public void deleteManualPreviews() { - disposePreviews(); - selectMode(NONE); - mCanvas.setFitScale(true /* onlyZoomOut */, true /*allowZoomIn*/); - - if (mManualList != null) { - mManualList.delete(); - } - } - - /** Dispose all the previews */ - public void disposePreviews() { - if (mPreviews != null) { - List<RenderPreview> old = mPreviews; - mPreviews = null; - for (RenderPreview preview : old) { - preview.dispose(); - } - } - } - - /** - * Deletes the given preview - * - * @param preview the preview to be deleted - */ - public void deletePreview(RenderPreview preview) { - mPreviews.remove(preview); - preview.dispose(); - layout(true); - mCanvas.redraw(); - - if (mManualList != null) { - mManualList.remove(preview); - saveList(); - } - } - - /** - * Compute the total width required for the previews, including internal padding - * - * @return total width in pixels - */ - public int computePreviewWidth() { - int maxPreviewWidth = 0; - if (hasPreviews()) { - for (RenderPreview preview : mPreviews) { - maxPreviewWidth = Math.max(maxPreviewWidth, preview.getWidth()); - } - - if (maxPreviewWidth > 0) { - maxPreviewWidth += 2 * PREVIEW_HGAP; // 2x for left and right side - maxPreviewWidth += LARGE_SHADOWS ? SHADOW_SIZE : SMALL_SHADOW_SIZE; - } - - return maxPreviewWidth; - } - - return 0; - } - - /** - * Layout Algorithm. This sets the {@link RenderPreview#getX()} and - * {@link RenderPreview#getY()} coordinates of all the previews. It also - * marks previews as visible or invisible via - * {@link RenderPreview#setVisible(boolean)} according to their position and - * the current visible view port in the layout canvas. Finally, it also sets - * the {@code mLayoutHeight} field, such that the scrollbars can compute the - * right scrolled area, and that scrolling can cause render refreshes on - * views that are made visible. - * <p> - * This is not a traditional bin packing problem, because the objects to be - * packaged do not have a fixed size; we can scale them up and down in order - * to provide an "optimal" size. - * <p> - * See http://en.wikipedia.org/wiki/Packing_problem See - * http://en.wikipedia.org/wiki/Bin_packing_problem - */ - void layout(boolean refresh) { - mNeedLayout = false; - - if (mPreviews == null || mPreviews.isEmpty()) { - return; - } - - int scaledImageWidth = mHScale.getScaledImgSize(); - int scaledImageHeight = mVScale.getScaledImgSize(); - Rectangle clientArea = mCanvas.getClientArea(); - - if (!refresh && - (scaledImageWidth == mPrevImageWidth - && scaledImageHeight == mPrevImageHeight - && clientArea.width == mPrevCanvasWidth - && clientArea.height == mPrevCanvasHeight)) { - // No change - return; - } - - mPrevImageWidth = scaledImageWidth; - mPrevImageHeight = scaledImageHeight; - mPrevCanvasWidth = clientArea.width; - mPrevCanvasHeight = clientArea.height; - - if (mListener == null) { - mListener = new ScrollBarListener(); - mCanvas.getVerticalBar().addSelectionListener(mListener); - } - - beginRenderScheduling(); - - mLayoutHeight = 0; - - if (previewsHaveIdenticalSize() || fixedOrder()) { - // If all the preview boxes are of identical sizes, or if the order is predetermined, - // just lay them out in rows. - rowLayout(); - } else if (previewsFit()) { - layoutFullFit(); - } else { - rowLayout(); - } - - mCanvas.updateScrollBars(); - } - - /** - * Performs a simple layout where the views are laid out in a row, wrapping - * around the top left canvas image. - */ - private void rowLayout() { - // TODO: Separate layout heuristics for portrait and landscape orientations (though - // it also depends on the dimensions of the canvas window, which determines the - // shape of the leftover space) - - int scaledImageWidth = mHScale.getScaledImgSize(); - int scaledImageHeight = mVScale.getScaledImgSize(); - Rectangle clientArea = mCanvas.getClientArea(); - - int availableWidth = clientArea.x + clientArea.width - getX(); - int availableHeight = clientArea.y + clientArea.height - getY(); - int maxVisibleY = clientArea.y + clientArea.height; - - int bottomBorder = scaledImageHeight; - int rightHandSide = scaledImageWidth + PREVIEW_HGAP; - int nextY = 0; - - // First lay out images across the top right hand side - int x = rightHandSide; - int y = 0; - boolean wrapped = false; - - int vgap = PREVIEW_VGAP; - for (RenderPreview preview : mPreviews) { - // If we have forked previews, double the vgap to allow space for two labels - if (preview.isForked()) { - vgap *= 2; - break; - } - } - - List<RenderPreview> aspectOrder; - if (!fixedOrder()) { - aspectOrder = new ArrayList<RenderPreview>(mPreviews); - Collections.sort(aspectOrder, RenderPreview.INCREASING_ASPECT_RATIO); - } else { - aspectOrder = mPreviews; - } - - for (RenderPreview preview : aspectOrder) { - if (x > 0 && x + preview.getWidth() > availableWidth) { - x = rightHandSide; - int prevY = y; - y = nextY; - if ((prevY <= bottomBorder || - y <= bottomBorder) - && Math.max(nextY, y + preview.getHeight()) > bottomBorder) { - // If there's really no visible room below, don't bother - // Similarly, don't wrap individually scaled views - if (bottomBorder < availableHeight - 40 && preview.getScale() < 1.2) { - // If it's closer to the top row than the bottom, just - // mark the next row for left justify instead - if (bottomBorder - y > y + preview.getHeight() - bottomBorder) { - rightHandSide = 0; - wrapped = true; - } else if (!wrapped) { - y = nextY = Math.max(nextY, bottomBorder + vgap); - x = rightHandSide = 0; - wrapped = true; - } - } - } - } - if (x > 0 && y <= bottomBorder - && Math.max(nextY, y + preview.getHeight()) > bottomBorder) { - if (clientArea.height - bottomBorder < preview.getHeight()) { - // No room below the device on the left; just continue on the - // bottom row - } else if (preview.getScale() < 1.2) { - if (bottomBorder - y > y + preview.getHeight() - bottomBorder) { - rightHandSide = 0; - wrapped = true; - } else { - y = nextY = Math.max(nextY, bottomBorder + vgap); - x = rightHandSide = 0; - wrapped = true; - } - } - } - - preview.setPosition(x, y); - - if (y > maxVisibleY && maxVisibleY > 0) { - preview.setVisible(false); - } else if (!preview.isVisible()) { - preview.setVisible(true); - } - - x += preview.getWidth(); - x += PREVIEW_HGAP; - nextY = Math.max(nextY, y + preview.getHeight() + vgap); - } - - mLayoutHeight = nextY; - } - - private boolean fixedOrder() { - return mMode == SCREENS; - } - - /** Returns true if all the previews have the same identical size */ - private boolean previewsHaveIdenticalSize() { - if (!hasPreviews()) { - return true; - } - - Iterator<RenderPreview> iterator = mPreviews.iterator(); - RenderPreview first = iterator.next(); - int width = first.getWidth(); - int height = first.getHeight(); - - while (iterator.hasNext()) { - RenderPreview preview = iterator.next(); - if (width != preview.getWidth() || height != preview.getHeight()) { - return false; - } - } - - return true; - } - - /** Returns true if all the previews can fully fit in the available space */ - private boolean previewsFit() { - int scaledImageWidth = mHScale.getScaledImgSize(); - int scaledImageHeight = mVScale.getScaledImgSize(); - Rectangle clientArea = mCanvas.getClientArea(); - int availableWidth = clientArea.x + clientArea.width - getX(); - int availableHeight = clientArea.y + clientArea.height - getY(); - int bottomBorder = scaledImageHeight; - int rightHandSide = scaledImageWidth + PREVIEW_HGAP; - - // First see if we can fit everything; if so, we can try to make the layouts - // larger such that they fill up all the available space - long availableArea = rightHandSide * bottomBorder + - availableWidth * (Math.max(0, availableHeight - bottomBorder)); - - long requiredArea = 0; - for (RenderPreview preview : mPreviews) { - // Note: This does not include individual preview scale; the layout - // algorithm itself may be tweaking the scales to fit elements within - // the layout - requiredArea += preview.getArea(); - } - - return requiredArea * sScale < availableArea; - } - - private void layoutFullFit() { - int scaledImageWidth = mHScale.getScaledImgSize(); - int scaledImageHeight = mVScale.getScaledImgSize(); - Rectangle clientArea = mCanvas.getClientArea(); - int availableWidth = clientArea.x + clientArea.width - getX(); - int availableHeight = clientArea.y + clientArea.height - getY(); - int maxVisibleY = clientArea.y + clientArea.height; - int bottomBorder = scaledImageHeight; - int rightHandSide = scaledImageWidth + PREVIEW_HGAP; - - int minWidth = Integer.MAX_VALUE; - int minHeight = Integer.MAX_VALUE; - for (RenderPreview preview : mPreviews) { - minWidth = Math.min(minWidth, preview.getWidth()); - minHeight = Math.min(minHeight, preview.getHeight()); - } - - BinPacker packer = new BinPacker(minWidth, minHeight); - - // TODO: Instead of this, just start with client area and occupy scaled image size! - - // Add in gap on right and bottom since we'll add that requirement on the width and - // height rectangles too (for spacing) - packer.addSpace(new Rect(rightHandSide, 0, - availableWidth - rightHandSide + PREVIEW_HGAP, - availableHeight + PREVIEW_VGAP)); - if (maxVisibleY > bottomBorder) { - packer.addSpace(new Rect(0, bottomBorder + PREVIEW_VGAP, - availableWidth + PREVIEW_HGAP, maxVisibleY - bottomBorder + PREVIEW_VGAP)); - } - - // TODO: Sort previews first before attempting to position them? - - ArrayList<RenderPreview> aspectOrder = new ArrayList<RenderPreview>(mPreviews); - Collections.sort(aspectOrder, RenderPreview.INCREASING_ASPECT_RATIO); - - for (RenderPreview preview : aspectOrder) { - int previewWidth = preview.getWidth(); - int previewHeight = preview.getHeight(); - previewHeight += PREVIEW_VGAP; - if (preview.isForked()) { - previewHeight += PREVIEW_VGAP; - } - previewWidth += PREVIEW_HGAP; - // title height? how do I account for that? - Rect position = packer.occupy(previewWidth, previewHeight); - if (position != null) { - preview.setPosition(position.x, position.y); - preview.setVisible(true); - } else { - // Can't fit: give up and do plain row layout - rowLayout(); - return; - } - } - - mLayoutHeight = availableHeight; - } - /** - * Paints the configuration previews - * - * @param gc the graphics context to paint into - */ - void paint(GC gc) { - if (hasPreviews()) { - // Ensure up to date at all times; consider moving if it's too expensive - layout(mNeedLayout); - if (mNeedRender) { - renderPreviews(); - } - if (mNeedZoom) { - boolean allowZoomIn = true /*mMode == NONE*/; - mCanvas.setFitScale(false /*onlyZoomOut*/, allowZoomIn); - mNeedZoom = false; - } - int rootX = getX(); - int rootY = getY(); - - for (RenderPreview preview : mPreviews) { - if (preview.isVisible()) { - int x = rootX + preview.getX(); - int y = rootY + preview.getY(); - preview.paint(gc, x, y); - } - } - - RenderPreview preview = mCanvas.getPreview(); - if (preview != null) { - String displayName = null; - Configuration configuration = preview.getConfiguration(); - if (configuration instanceof VaryingConfiguration) { - // Use override flags from stashed preview, but configuration - // data from live (not varying) configured configuration - VaryingConfiguration cfg = (VaryingConfiguration) configuration; - int flags = cfg.getAlternateFlags() | cfg.getOverrideFlags(); - displayName = NestedConfiguration.computeDisplayName(flags, - getChooser().getConfiguration()); - } else if (configuration instanceof NestedConfiguration) { - int flags = ((NestedConfiguration) configuration).getOverrideFlags(); - displayName = NestedConfiguration.computeDisplayName(flags, - getChooser().getConfiguration()); - } else { - displayName = configuration.getDisplayName(); - } - if (displayName != null) { - CanvasTransform hi = mHScale; - CanvasTransform vi = mVScale; - - int destX = hi.translate(0); - int destY = vi.translate(0); - int destWidth = hi.getScaledImgSize(); - int destHeight = vi.getScaledImgSize(); - - int x = destX + destWidth / 2 - preview.getWidth() / 2; - int y = destY + destHeight; - - preview.paintTitle(gc, x, y, false /*showFile*/, displayName); - } - } - - // Zoom overlay - int x = getZoomX(); - if (x > 0) { - int y = getZoomY(); - int oldAlpha = gc.getAlpha(); - - // Paint background oval rectangle behind the zoom and close icons - gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY)); - gc.setAlpha(128); - int padding = 3; - int arc = 5; - gc.fillRoundRectangle(x - padding, y - padding, - ZOOM_ICON_WIDTH + 2 * padding, - 4 * ZOOM_ICON_HEIGHT + 2 * padding, arc, arc); - - gc.setAlpha(255); - IconFactory iconFactory = IconFactory.getInstance(); - Image zoomOut = iconFactory.getIcon("zoomminus"); //$NON-NLS-1$); - Image zoomIn = iconFactory.getIcon("zoomplus"); //$NON-NLS-1$); - Image zoom100 = iconFactory.getIcon("zoom100"); //$NON-NLS-1$); - Image close = iconFactory.getIcon("close"); //$NON-NLS-1$); - - gc.drawImage(zoomIn, x, y); - y += ZOOM_ICON_HEIGHT; - gc.drawImage(zoomOut, x, y); - y += ZOOM_ICON_HEIGHT; - gc.drawImage(zoom100, x, y); - y += ZOOM_ICON_HEIGHT; - gc.drawImage(close, x, y); - y += ZOOM_ICON_HEIGHT; - gc.setAlpha(oldAlpha); - } - } else if (mMode == CUSTOM) { - int rootX = getX(); - rootX += mHScale.getScaledImgSize(); - rootX += 2 * PREVIEW_HGAP; - int rootY = getY(); - rootY += 20; - gc.setFont(mCanvas.getFont()); - gc.setForeground(mCanvas.getDisplay().getSystemColor(SWT.COLOR_BLACK)); - gc.drawText("Add previews with \"Add as Thumbnail\"\nin the configuration menu", - rootX, rootY, true); - } - - if (mAnimation != null) { - mAnimation.tick(gc); - } - } - - private void addPreview(@NonNull RenderPreview preview) { - if (mPreviews == null) { - mPreviews = Lists.newArrayList(); - } - mPreviews.add(preview); - } - - /** Adds the current configuration as a new configuration preview */ - public void addAsThumbnail() { - ConfigurationChooser chooser = getChooser(); - String name = chooser.getConfiguration().getDisplayName(); - if (name == null || name.isEmpty()) { - name = getUniqueName(); - } - InputDialog d = new InputDialog( - AdtPlugin.getShell(), - "Add as Thumbnail Preview", // title - "Name of thumbnail:", - name, - null); - if (d.open() == Window.OK) { - selectMode(CUSTOM); - - String newName = d.getValue(); - // Create a new configuration from the current settings in the composite - Configuration configuration = Configuration.copy(chooser.getConfiguration()); - configuration.setDisplayName(newName); - - RenderPreview preview = RenderPreview.create(this, configuration); - addPreview(preview); - - layout(true); - beginRenderScheduling(); - scheduleRender(preview); - mCanvas.setFitScale(true /* onlyZoomOut */, false /*allowZoomIn*/); - - if (mManualList == null) { - loadList(); - } - if (mManualList != null) { - mManualList.add(preview); - saveList(); - } - } - } - - /** - * Computes a unique new name for a configuration preview that represents - * the current, default configuration - * - * @return a unique name - */ - private String getUniqueName() { - if (mPreviews == null || mPreviews.isEmpty()) { - // NO, not for the first preview! - return "Config1"; - } - - Set<String> names = new HashSet<String>(mPreviews.size()); - for (RenderPreview preview : mPreviews) { - names.add(preview.getDisplayName()); - } - - int index = 2; - while (true) { - String name = String.format("Config%1$d", index); - if (!names.contains(name)) { - return name; - } - index++; - } - } - - /** Generates a bunch of default configuration preview thumbnails */ - public void addDefaultPreviews() { - ConfigurationChooser chooser = getChooser(); - Configuration parent = chooser.getConfiguration(); - if (parent instanceof NestedConfiguration) { - parent = ((NestedConfiguration) parent).getParent(); - } - if (mCanvas.getImageOverlay().getImage() != null) { - // Create Language variation - createLocaleVariation(chooser, parent); - - // Vary screen size - // TODO: Be smarter here: Pick a screen that is both as differently as possible - // from the current screen as well as also supported. So consider - // things like supported screens, targetSdk etc. - createScreenVariations(parent); - - // Vary orientation - createStateVariation(chooser, parent); - - // Vary render target - createRenderTargetVariation(chooser, parent); - } - - // Also add in include-context previews, if any - addIncludedInPreviews(); - - // Make a placeholder preview for the current screen, in case we switch from it - RenderPreview preview = RenderPreview.create(this, parent); - mCanvas.setPreview(preview); - - sortPreviewsByOrientation(); - } - - private void createRenderTargetVariation(ConfigurationChooser chooser, Configuration parent) { - /* This is disabled for now: need to load multiple versions of layoutlib. - When I did this, there seemed to be some drug interactions between - them, and I would end up with NPEs in layoutlib code which normally works. - VaryingConfiguration configuration = - VaryingConfiguration.create(chooser, parent); - configuration.setAlternatingTarget(true); - configuration.syncFolderConfig(); - addPreview(RenderPreview.create(this, configuration)); - */ - } - - private void createStateVariation(ConfigurationChooser chooser, Configuration parent) { - State currentState = parent.getDeviceState(); - State nextState = parent.getNextDeviceState(currentState); - if (nextState != currentState) { - VaryingConfiguration configuration = - VaryingConfiguration.create(chooser, parent); - configuration.setAlternateDeviceState(true); - configuration.syncFolderConfig(); - addPreview(RenderPreview.create(this, configuration)); - } - } - - private void createLocaleVariation(ConfigurationChooser chooser, Configuration parent) { - LocaleQualifier currentLanguage = parent.getLocale().qualifier; - for (Locale locale : chooser.getLocaleList()) { - LocaleQualifier qualifier = locale.qualifier; - if (!qualifier.getLanguage().equals(currentLanguage.getLanguage())) { - VaryingConfiguration configuration = - VaryingConfiguration.create(chooser, parent); - configuration.setAlternateLocale(true); - configuration.syncFolderConfig(); - addPreview(RenderPreview.create(this, configuration)); - break; - } - } - } - - private void createScreenVariations(Configuration parent) { - ConfigurationChooser chooser = getChooser(); - VaryingConfiguration configuration; - - configuration = VaryingConfiguration.create(chooser, parent); - configuration.setVariation(0); - configuration.setAlternateDevice(true); - configuration.syncFolderConfig(); - addPreview(RenderPreview.create(this, configuration)); - - configuration = VaryingConfiguration.create(chooser, parent); - configuration.setVariation(1); - configuration.setAlternateDevice(true); - configuration.syncFolderConfig(); - addPreview(RenderPreview.create(this, configuration)); - } - - /** - * Returns the current mode as seen by this {@link RenderPreviewManager}. - * Note that it may not yet have been synced with the global mode kept in - * {@link AdtPrefs#getRenderPreviewMode()}. - * - * @return the current preview mode - */ - @NonNull - public RenderPreviewMode getMode() { - return mMode; - } - - /** - * Update the set of previews for the current mode - * - * @param force force a refresh even if the preview type has not changed - * @return true if the views were recomputed, false if the previews were - * already showing and the mode not changed - */ - public boolean recomputePreviews(boolean force) { - RenderPreviewMode newMode = AdtPrefs.getPrefs().getRenderPreviewMode(); - if (newMode == mMode && !force - && (mRevision == sRevision - || mMode == NONE - || mMode == CUSTOM)) { - return false; - } - - RenderPreviewMode oldMode = mMode; - mMode = newMode; - mRevision = sRevision; - - sScale = 1.0; - disposePreviews(); - - switch (mMode) { - case DEFAULT: - addDefaultPreviews(); - break; - case INCLUDES: - addIncludedInPreviews(); - break; - case LOCALES: - addLocalePreviews(); - break; - case SCREENS: - addScreenSizePreviews(); - break; - case VARIATIONS: - addVariationPreviews(); - break; - case CUSTOM: - addManualPreviews(); - break; - case NONE: - // Can't just set mNeedZoom because with no previews, the paint - // method does nothing - mCanvas.setFitScale(false /*onlyZoomOut*/, true /*allowZoomIn*/); - break; - default: - assert false : mMode; - } - - // We schedule layout for the next redraw rather than process it here immediately; - // not only does this let us avoid doing work for windows where the tab is in the - // background, but when a file is opened we may not know the size of the canvas - // yet, and the layout methods need it in order to do a good job. By the time - // the canvas is painted, we have accurate bounds. - mNeedLayout = mNeedRender = true; - mCanvas.redraw(); - - if (oldMode != mMode && (oldMode == NONE || mMode == NONE)) { - // If entering or exiting preview mode: updating padding which is compressed - // only in preview mode. - mCanvas.getHorizontalTransform().refresh(); - mCanvas.getVerticalTransform().refresh(); - } - - return true; - } - - /** - * Sets the new render preview mode to use - * - * @param mode the new mode - */ - public void selectMode(@NonNull RenderPreviewMode mode) { - if (mode != mMode) { - AdtPrefs.getPrefs().setPreviewMode(mode); - recomputePreviews(false); - } - } - - /** Similar to {@link #addDefaultPreviews()} but for locales */ - public void addLocalePreviews() { - - ConfigurationChooser chooser = getChooser(); - List<Locale> locales = chooser.getLocaleList(); - Configuration parent = chooser.getConfiguration(); - - for (Locale locale : locales) { - if (!locale.hasLanguage() && !locale.hasRegion()) { - continue; - } - NestedConfiguration configuration = NestedConfiguration.create(chooser, parent); - configuration.setOverrideLocale(true); - configuration.setLocale(locale, false); - - String displayName = ConfigurationChooser.getLocaleLabel(chooser, locale, false); - assert displayName != null; // it's never non null when locale is non null - configuration.setDisplayName(displayName); - - addPreview(RenderPreview.create(this, configuration)); - } - - // Make a placeholder preview for the current screen, in case we switch from it - Configuration configuration = parent; - Locale locale = configuration.getLocale(); - String label = ConfigurationChooser.getLocaleLabel(chooser, locale, false); - if (label == null) { - label = "default"; - } - configuration.setDisplayName(label); - RenderPreview preview = RenderPreview.create(this, parent); - if (preview != null) { - mCanvas.setPreview(preview); - } - - // No need to sort: they should all be identical - } - - /** Similar to {@link #addDefaultPreviews()} but for screen sizes */ - public void addScreenSizePreviews() { - ConfigurationChooser chooser = getChooser(); - Collection<Device> devices = chooser.getDevices(); - Configuration configuration = chooser.getConfiguration(); - boolean canScaleNinePatch = configuration.supports(Capability.FIXED_SCALABLE_NINE_PATCH); - - // Rearrange the devices a bit such that the most interesting devices bubble - // to the front - // 10" tablet, 7" tablet, reference phones, tiny phone, and in general the first - // version of each seen screen size - List<Device> sorted = new ArrayList<Device>(devices); - Set<ScreenSize> seenSizes = new HashSet<ScreenSize>(); - State currentState = configuration.getDeviceState(); - String currentStateName = currentState != null ? currentState.getName() : ""; - - for (int i = 0, n = sorted.size(); i < n; i++) { - Device device = sorted.get(i); - boolean interesting = false; - - State state = device.getState(currentStateName); - if (state == null) { - state = device.getAllStates().get(0); - } - - if (device.getName().startsWith("Nexus ") //$NON-NLS-1$ - || device.getName().endsWith(" Nexus")) { //$NON-NLS-1$ - // Not String#contains("Nexus") because that would also pick up all the generic - // entries ("3.7in WVGA (Nexus One)") so we'd have them duplicated - interesting = true; - } - - FolderConfiguration c = DeviceConfigHelper.getFolderConfig(state); - if (c != null) { - ScreenSizeQualifier sizeQualifier = c.getScreenSizeQualifier(); - if (sizeQualifier != null) { - ScreenSize size = sizeQualifier.getValue(); - if (!seenSizes.contains(size)) { - seenSizes.add(size); - interesting = true; - } - } - - // Omit LDPI, not really used anymore - DensityQualifier density = c.getDensityQualifier(); - if (density != null) { - Density d = density.getValue(); - if (d == Density.LOW) { - interesting = false; - } - - if (!canScaleNinePatch && d == Density.TV) { - interesting = false; - } - } - } - - if (interesting) { - NestedConfiguration screenConfig = NestedConfiguration.create(chooser, - configuration); - screenConfig.setOverrideDevice(true); - screenConfig.setDevice(device, true); - screenConfig.syncFolderConfig(); - screenConfig.setDisplayName(ConfigurationChooser.getDeviceLabel(device, true)); - addPreview(RenderPreview.create(this, screenConfig)); - } - } - - // Sorted by screen size, in decreasing order - sortPreviewsByScreenSize(); - } - - /** - * Previews this layout as included in other layouts - */ - public void addIncludedInPreviews() { - ConfigurationChooser chooser = getChooser(); - IProject project = chooser.getProject(); - if (project == null) { - return; - } - IncludeFinder finder = IncludeFinder.get(project); - - final List<Reference> includedBy = finder.getIncludedBy(chooser.getEditedFile()); - - if (includedBy == null || includedBy.isEmpty()) { - // TODO: Generate some useful defaults, such as including it in a ListView - // as the list item layout? - return; - } - - for (final Reference reference : includedBy) { - String title = reference.getDisplayName(); - Configuration config = Configuration.create(chooser.getConfiguration(), - reference.getFile()); - RenderPreview preview = RenderPreview.create(this, config); - preview.setDisplayName(title); - preview.setIncludedWithin(reference); - - addPreview(preview); - } - - sortPreviewsByOrientation(); - } - - /** - * Previews this layout as included in other layouts - */ - public void addVariationPreviews() { - ConfigurationChooser chooser = getChooser(); - - IFile file = chooser.getEditedFile(); - List<IFile> variations = AdtUtils.getResourceVariations(file, false /*includeSelf*/); - - // Sort by parent folder - Collections.sort(variations, new Comparator<IFile>() { - @Override - public int compare(IFile file1, IFile file2) { - return file1.getParent().getName().compareTo(file2.getParent().getName()); - } - }); - - Configuration currentConfig = chooser.getConfiguration(); - - for (IFile variation : variations) { - String title = variation.getParent().getName(); - Configuration config = Configuration.create(chooser.getConfiguration(), variation); - config.setTheme(currentConfig.getTheme()); - config.setActivity(currentConfig.getActivity()); - RenderPreview preview = RenderPreview.create(this, config); - preview.setDisplayName(title); - preview.setAlternateInput(variation); - - addPreview(preview); - } - - sortPreviewsByOrientation(); - } - - /** - * Previews this layout using a custom configured set of layouts - */ - public void addManualPreviews() { - if (mManualList == null) { - loadList(); - } else { - mPreviews = mManualList.createPreviews(mCanvas); - } - } - - private void loadList() { - IProject project = getChooser().getProject(); - if (project == null) { - return; - } - - if (mManualList == null) { - mManualList = RenderPreviewList.get(project); - } - - try { - mManualList.load(getChooser().getDevices()); - mPreviews = mManualList.createPreviews(mCanvas); - } catch (IOException e) { - AdtPlugin.log(e, null); - } - } - - private void saveList() { - if (mManualList != null) { - try { - mManualList.save(); - } catch (IOException e) { - AdtPlugin.log(e, null); - } - } - } - - void rename(ConfigurationDescription description, String newName) { - IProject project = getChooser().getProject(); - if (project == null) { - return; - } - - if (mManualList == null) { - mManualList = RenderPreviewList.get(project); - } - description.displayName = newName; - saveList(); - } - - - /** - * Notifies that the main configuration has changed. - * - * @param flags the change flags, a bitmask corresponding to the - * {@code CHANGE_} constants in {@link ConfigurationClient} - */ - public void configurationChanged(int flags) { - // Similar to renderPreviews, but only acts on incomplete previews - if (hasPreviews()) { - // Do zoomed images first - beginRenderScheduling(); - for (RenderPreview preview : mPreviews) { - if (preview.getScale() > 1.2) { - preview.configurationChanged(flags); - } - } - for (RenderPreview preview : mPreviews) { - if (preview.getScale() <= 1.2) { - preview.configurationChanged(flags); - } - } - RenderPreview preview = mCanvas.getPreview(); - if (preview != null) { - preview.configurationChanged(flags); - preview.dispose(); - } - mNeedLayout = true; - mCanvas.redraw(); - } - } - - /** Updates the configuration preview thumbnails */ - public void renderPreviews() { - if (hasPreviews()) { - beginRenderScheduling(); - - // Process in visual order - ArrayList<RenderPreview> visualOrder = new ArrayList<RenderPreview>(mPreviews); - Collections.sort(visualOrder, RenderPreview.VISUAL_ORDER); - - // Do zoomed images first - for (RenderPreview preview : visualOrder) { - if (preview.getScale() > 1.2 && preview.isVisible()) { - scheduleRender(preview); - } - } - // Non-zoomed images - for (RenderPreview preview : visualOrder) { - if (preview.getScale() <= 1.2 && preview.isVisible()) { - scheduleRender(preview); - } - } - } - - mNeedRender = false; - } - - private int mPendingRenderCount; - - /** - * Reset rendering scheduling. The next render request will be scheduled - * after a single delay unit. - */ - public void beginRenderScheduling() { - mPendingRenderCount = 0; - } - - /** - * Schedule rendering the given preview. Each successive call will add an additional - * delay unit to the schedule from the previous {@link #scheduleRender(RenderPreview)} - * call, until {@link #beginRenderScheduling()} is called again. - * - * @param preview the preview to render - */ - public void scheduleRender(@NonNull RenderPreview preview) { - mPendingRenderCount++; - preview.render(mPendingRenderCount * RENDER_DELAY); - } - - /** - * Switch to the given configuration preview - * - * @param preview the preview to switch to - */ - public void switchTo(@NonNull RenderPreview preview) { - IFile input = preview.getAlternateInput(); - if (input != null) { - IWorkbenchPartSite site = mCanvas.getEditorDelegate().getEditor().getSite(); - try { - // This switches to the given file, but the file might not have - // an identical configuration to what was shown in the preview. - // For example, while viewing a 10" layout-xlarge file, it might - // show a preview for a 5" version tied to the default layout. If - // you click on it, it will open the default layout file, but it might - // be using a different screen size; any of those that match the - // default layout, say a 3.8". - // - // Thus, we need to also perform a screen size sync first - Configuration configuration = preview.getConfiguration(); - boolean setSize = false; - if (configuration instanceof NestedConfiguration) { - NestedConfiguration nestedConfig = (NestedConfiguration) configuration; - setSize = nestedConfig.isOverridingDevice(); - if (configuration instanceof VaryingConfiguration) { - VaryingConfiguration c = (VaryingConfiguration) configuration; - setSize |= c.isAlternatingDevice(); - } - - if (setSize) { - ConfigurationChooser chooser = getChooser(); - IFile editedFile = chooser.getEditedFile(); - if (editedFile != null) { - chooser.syncToVariations(CFG_DEVICE|CFG_DEVICE_STATE, - editedFile, configuration, false, false); - } - } - } - - IDE.openEditor(site.getWorkbenchWindow().getActivePage(), input, - CommonXmlEditor.ID); - } catch (PartInitException e) { - AdtPlugin.log(e, null); - } - return; - } - - GraphicalEditorPart editor = mCanvas.getEditorDelegate().getGraphicalEditor(); - ConfigurationChooser chooser = editor.getConfigurationChooser(); - - Configuration originalConfiguration = chooser.getConfiguration(); - - // The new configuration is the configuration which will become the configuration - // in the layout editor's chooser - Configuration previewConfiguration = preview.getConfiguration(); - Configuration newConfiguration = previewConfiguration; - if (newConfiguration instanceof NestedConfiguration) { - // Should never use a complementing configuration for the main - // rendering's configuration; instead, create a new configuration - // with a snapshot of the configuration's current values - newConfiguration = Configuration.copy(previewConfiguration); - - // Remap all the previews to be parented to this new copy instead - // of the old one (which is no longer controlled by the chooser) - for (RenderPreview p : mPreviews) { - Configuration configuration = p.getConfiguration(); - if (configuration instanceof NestedConfiguration) { - NestedConfiguration nested = (NestedConfiguration) configuration; - nested.setParent(newConfiguration); - } - } - } - - // Make a preview for the configuration which *was* showing in the - // chooser up until this point: - RenderPreview newPreview = mCanvas.getPreview(); - if (newPreview == null) { - newPreview = RenderPreview.create(this, originalConfiguration); - } - - // Update its configuration such that it is complementing or inheriting - // from the new chosen configuration - if (previewConfiguration instanceof VaryingConfiguration) { - VaryingConfiguration varying = VaryingConfiguration.create( - (VaryingConfiguration) previewConfiguration, - newConfiguration); - varying.updateDisplayName(); - originalConfiguration = varying; - newPreview.setConfiguration(originalConfiguration); - } else if (previewConfiguration instanceof NestedConfiguration) { - NestedConfiguration nested = NestedConfiguration.create( - (NestedConfiguration) previewConfiguration, - originalConfiguration, - newConfiguration); - nested.setDisplayName(nested.computeDisplayName()); - originalConfiguration = nested; - newPreview.setConfiguration(originalConfiguration); - } - - // Replace clicked preview with preview of the formerly edited main configuration - // This doesn't work yet because the image overlay has had its image - // replaced by the configuration previews! I should make a list of them - //newPreview.setFullImage(mImageOverlay.getAwtImage()); - for (int i = 0, n = mPreviews.size(); i < n; i++) { - if (preview == mPreviews.get(i)) { - mPreviews.set(i, newPreview); - break; - } - } - - // Stash the corresponding preview (not active) on the canvas so we can - // retrieve it if clicking to some other preview later - mCanvas.setPreview(preview); - preview.setVisible(false); - - // Switch to the configuration from the clicked preview (though it's - // most likely a copy, see above) - chooser.setConfiguration(newConfiguration); - editor.changed(MASK_ALL); - - // Scroll to the top again, if necessary - mCanvas.getVerticalBar().setSelection(mCanvas.getVerticalBar().getMinimum()); - - mNeedLayout = mNeedZoom = true; - mCanvas.redraw(); - mAnimation = new SwapAnimation(preview, newPreview); - } - - /** - * Gets the preview at the given location, or null if none. This is - * currently deeply tied to where things are painted in onPaint(). - */ - RenderPreview getPreview(ControlPoint mousePos) { - if (hasPreviews()) { - int rootX = getX(); - if (mousePos.x < rootX) { - return null; - } - int rootY = getY(); - - for (RenderPreview preview : mPreviews) { - int x = rootX + preview.getX(); - int y = rootY + preview.getY(); - if (mousePos.x >= x && mousePos.x <= x + preview.getWidth()) { - if (mousePos.y >= y && mousePos.y <= y + preview.getHeight()) { - return preview; - } - } - } - } - - return null; - } - - private int getX() { - return mHScale.translate(0); - } - - private int getY() { - return mVScale.translate(0); - } - - private int getZoomX() { - Rectangle clientArea = mCanvas.getClientArea(); - int x = clientArea.x + clientArea.width - ZOOM_ICON_WIDTH; - if (x < mHScale.getScaledImgSize() + PREVIEW_HGAP) { - // No visible previews because the main image is zoomed too far - return -1; - } - - return x - 6; - } - - private int getZoomY() { - Rectangle clientArea = mCanvas.getClientArea(); - return clientArea.y + 5; - } - - /** - * Returns the height of the layout - * - * @return the height - */ - public int getHeight() { - return mLayoutHeight; - } - - /** - * Notifies that preview manager that the mouse cursor has moved to the - * given control position within the layout canvas - * - * @param mousePos the mouse position, relative to the layout canvas - */ - public void moved(ControlPoint mousePos) { - RenderPreview hovered = getPreview(mousePos); - if (hovered != mActivePreview) { - if (mActivePreview != null) { - mActivePreview.setActive(false); - } - mActivePreview = hovered; - if (mActivePreview != null) { - mActivePreview.setActive(true); - } - mCanvas.redraw(); - } - } - - /** - * Notifies that preview manager that the mouse cursor has entered the layout canvas - * - * @param mousePos the mouse position, relative to the layout canvas - */ - public void enter(ControlPoint mousePos) { - moved(mousePos); - } - - /** - * Notifies that preview manager that the mouse cursor has exited the layout canvas - * - * @param mousePos the mouse position, relative to the layout canvas - */ - public void exit(ControlPoint mousePos) { - if (mActivePreview != null) { - mActivePreview.setActive(false); - } - mActivePreview = null; - mCanvas.redraw(); - } - - /** - * Process a mouse click, and return true if it was handled by this manager - * (e.g. the click was on a preview) - * - * @param mousePos the mouse position where the click occurred - * @return true if the click occurred over a preview and was handled, false otherwise - */ - public boolean click(ControlPoint mousePos) { - // Clicked zoom? - int x = getZoomX(); - if (x > 0) { - if (mousePos.x >= x && mousePos.x <= x + ZOOM_ICON_WIDTH) { - int y = getZoomY(); - if (mousePos.y >= y && mousePos.y <= y + 4 * ZOOM_ICON_HEIGHT) { - if (mousePos.y < y + ZOOM_ICON_HEIGHT) { - zoomIn(); - } else if (mousePos.y < y + 2 * ZOOM_ICON_HEIGHT) { - zoomOut(); - } else if (mousePos.y < y + 3 * ZOOM_ICON_HEIGHT) { - zoomReset(); - } else { - selectMode(NONE); - } - return true; - } - } - } - - RenderPreview preview = getPreview(mousePos); - if (preview != null) { - boolean handled = preview.click(mousePos.x - getX() - preview.getX(), - mousePos.y - getY() - preview.getY()); - if (handled) { - // In case layout was performed, there could be a new preview - // under this coordinate now, so make sure it's hover etc - // shows up - moved(mousePos); - return true; - } - } - - return false; - } - - /** - * Returns true if there are thumbnail previews - * - * @return true if thumbnails are being shown - */ - public boolean hasPreviews() { - return mPreviews != null && !mPreviews.isEmpty(); - } - - - private void sortPreviewsByScreenSize() { - if (mPreviews != null) { - Collections.sort(mPreviews, new Comparator<RenderPreview>() { - @Override - public int compare(RenderPreview preview1, RenderPreview preview2) { - Configuration config1 = preview1.getConfiguration(); - Configuration config2 = preview2.getConfiguration(); - Device device1 = config1.getDevice(); - Device device2 = config1.getDevice(); - if (device1 != null && device2 != null) { - Screen screen1 = device1.getDefaultHardware().getScreen(); - Screen screen2 = device2.getDefaultHardware().getScreen(); - if (screen1 != null && screen2 != null) { - double delta = screen1.getDiagonalLength() - - screen2.getDiagonalLength(); - if (delta != 0.0) { - return (int) Math.signum(delta); - } else { - if (screen1.getPixelDensity() != screen2.getPixelDensity()) { - return screen1.getPixelDensity().compareTo( - screen2.getPixelDensity()); - } - } - } - - } - State state1 = config1.getDeviceState(); - State state2 = config2.getDeviceState(); - if (state1 != state2 && state1 != null && state2 != null) { - return state1.getName().compareTo(state2.getName()); - } - - return preview1.getDisplayName().compareTo(preview2.getDisplayName()); - } - }); - } - } - - private void sortPreviewsByOrientation() { - if (mPreviews != null) { - Collections.sort(mPreviews, new Comparator<RenderPreview>() { - @Override - public int compare(RenderPreview preview1, RenderPreview preview2) { - Configuration config1 = preview1.getConfiguration(); - Configuration config2 = preview2.getConfiguration(); - State state1 = config1.getDeviceState(); - State state2 = config2.getDeviceState(); - if (state1 != state2 && state1 != null && state2 != null) { - return state1.getName().compareTo(state2.getName()); - } - - return preview1.getDisplayName().compareTo(preview2.getDisplayName()); - } - }); - } - } - - /** - * Vertical scrollbar listener which updates render previews which are not - * visible and triggers a redraw - */ - private class ScrollBarListener implements SelectionListener { - @Override - public void widgetSelected(SelectionEvent e) { - if (mPreviews == null) { - return; - } - - ScrollBar bar = mCanvas.getVerticalBar(); - int selection = bar.getSelection(); - int thumb = bar.getThumb(); - int maxY = selection + thumb; - beginRenderScheduling(); - for (RenderPreview preview : mPreviews) { - if (!preview.isVisible() && preview.getY() <= maxY) { - preview.setVisible(true); - } - } - } - - @Override - public void widgetDefaultSelected(SelectionEvent e) { - } - } - - /** Animation overlay shown briefly after swapping two previews */ - private class SwapAnimation implements Runnable { - private long begin; - private long end; - private static final long DURATION = 400; // ms - private Rect initialRect1; - private Rect targetRect1; - private Rect initialRect2; - private Rect targetRect2; - private RenderPreview preview; - - SwapAnimation(RenderPreview preview1, RenderPreview preview2) { - begin = System.currentTimeMillis(); - end = begin + DURATION; - - initialRect1 = new Rect(preview1.getX(), preview1.getY(), - preview1.getWidth(), preview1.getHeight()); - - CanvasTransform hi = mCanvas.getHorizontalTransform(); - CanvasTransform vi = mCanvas.getVerticalTransform(); - initialRect2 = new Rect(hi.translate(0), vi.translate(0), - hi.getScaledImgSize(), vi.getScaledImgSize()); - preview = preview2; - } - - void tick(GC gc) { - long now = System.currentTimeMillis(); - if (now > end || mCanvas.isDisposed()) { - mAnimation = null; - return; - } - - CanvasTransform hi = mCanvas.getHorizontalTransform(); - CanvasTransform vi = mCanvas.getVerticalTransform(); - if (targetRect1 == null) { - targetRect1 = new Rect(hi.translate(0), vi.translate(0), - hi.getScaledImgSize(), vi.getScaledImgSize()); - } - double portion = (now - begin) / (double) DURATION; - Rect rect1 = new Rect( - (int) (portion * (targetRect1.x - initialRect1.x) + initialRect1.x), - (int) (portion * (targetRect1.y - initialRect1.y) + initialRect1.y), - (int) (portion * (targetRect1.w - initialRect1.w) + initialRect1.w), - (int) (portion * (targetRect1.h - initialRect1.h) + initialRect1.h)); - - if (targetRect2 == null) { - targetRect2 = new Rect(preview.getX(), preview.getY(), - preview.getWidth(), preview.getHeight()); - } - portion = (now - begin) / (double) DURATION; - Rect rect2 = new Rect( - (int) (portion * (targetRect2.x - initialRect2.x) + initialRect2.x), - (int) (portion * (targetRect2.y - initialRect2.y) + initialRect2.y), - (int) (portion * (targetRect2.w - initialRect2.w) + initialRect2.w), - (int) (portion * (targetRect2.h - initialRect2.h) + initialRect2.h)); - - gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GRAY)); - gc.drawRectangle(rect1.x, rect1.y, rect1.w, rect1.h); - gc.drawRectangle(rect2.x, rect2.y, rect2.w, rect2.h); - - mCanvas.getDisplay().timerExec(5, this); - } - - @Override - public void run() { - mCanvas.redraw(); - } - } - - /** - * Notifies the {@linkplain RenderPreviewManager} that the configuration used - * in the main chooser has been changed. This may require updating parent references - * in the preview configurations inheriting from it. - * - * @param oldConfiguration the previous configuration - * @param newConfiguration the new configuration in the chooser - */ - public void updateChooserConfig( - @NonNull Configuration oldConfiguration, - @NonNull Configuration newConfiguration) { - if (hasPreviews()) { - for (RenderPreview preview : mPreviews) { - Configuration configuration = preview.getConfiguration(); - if (configuration instanceof NestedConfiguration) { - NestedConfiguration nestedConfig = (NestedConfiguration) configuration; - if (nestedConfig.getParent() == oldConfiguration) { - nestedConfig.setParent(newConfiguration); - } - } - } - } - } -} |