diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src')
10 files changed, 279 insertions, 43 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/Pair.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/Pair.java index 950f6b9..e959321 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/Pair.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/Pair.java @@ -27,7 +27,7 @@ package com.android.ide.common.layout; * @param <S> The type of the first value * @param <T> The type of the second value */ -class Pair<S,T> { +public class Pair<S,T> { private final S mFirst; private final T mSecond; @@ -37,19 +37,35 @@ class Pair<S,T> { this.mSecond = second; } - /** Return the first item in the pair */ + /** + * Return the first item in the pair + * + * @return the first item in the pair + */ public S getFirst() { return mFirst; } - /** Return the second item in the pair */ + /** + * Return the second item in the pair + * + * @return the second item in the pair + */ public T getSecond() { return mSecond; } - /** Constructs a new pair of the given two objects, inferring generic types. */ - public static <S,T> Pair<S,T> of(S a, T b) { - return new Pair<S,T>(a,b); + /** + * Constructs a new pair of the given two objects, inferring generic types. + * + * @param first the first item to store in the pair + * @param second the second item to store in the pair + * @param <S> the type of the first item + * @param <T> the type of the second item + * @return a new pair wrapping the two items + */ + public static <S,T> Pair<S,T> of(S first, T second) { + return new Pair<S,T>(first,second); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasSelection.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasSelection.java index 3648258..de9abe9 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasSelection.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasSelection.java @@ -49,7 +49,8 @@ import java.util.List; /** * Creates a new {@link CanvasSelection} object. * @param canvasViewInfo The view info being selected. Must not be null. - * @param nodeFactory + * @param gre the rules engine + * @param nodeFactory the node factory */ public CanvasSelection(CanvasViewInfo canvasViewInfo, RulesEngine gre, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java index cb947d2..2c7bd73 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java @@ -33,6 +33,9 @@ import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.TypedEvent; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Rectangle; import java.util.ArrayList; import java.util.List; @@ -485,6 +488,8 @@ public class GestureManager { * {@inheritDoc} */ public void dragStart(DragSourceEvent e) { + LayoutPoint p = LayoutPoint.create(mCanvas, e); + // We need a selection (simple or multiple) to do any transfer. // If there's a selection *and* the cursor is over this selection, // use all the currently selected elements. @@ -497,8 +502,6 @@ public class GestureManager { if (!selections.isEmpty()) { // Is the cursor on top of a selected element? - LayoutPoint p = LayoutPoint.create(mCanvas, e); - boolean insideSelection = false; for (CanvasSelection cs : selections) { @@ -540,7 +543,6 @@ public class GestureManager { // If you are dragging a non-selected item, select it if (mDragSelection.isEmpty()) { - LayoutPoint p = ControlPoint.create(mCanvas, e).toLayout(); CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p); if (vi != null && !vi.isRoot()) { mCanvas.getSelectionManager().selectSingle(vi); @@ -551,10 +553,11 @@ public class GestureManager { SelectionManager.sanitize(mDragSelection); e.doit = !mDragSelection.isEmpty(); + int imageCount = mDragSelection.size(); if (e.doit) { mDragElements = CanvasSelection.getAsElements(mDragSelection); GlobalCanvasDragInfo.getInstance().startDrag(mDragElements, - mDragSelection.toArray(new CanvasSelection[mDragSelection.size()]), + mDragSelection.toArray(new CanvasSelection[imageCount]), mCanvas, new Runnable() { public void run() { mCanvas.getClipboardSupport().deleteSelection("Remove", @@ -565,7 +568,7 @@ public class GestureManager { // If you drag on the -background-, we make that into a marquee // selection - if (!e.doit || (mDragSelection.size() == 1 && mDragSelection.get(0).isRoot())) { + if (!e.doit || (imageCount == 1 && mDragSelection.get(0).isRoot())) { boolean toggle = (mLastStateMask & (SWT.CTRL | SWT.SHIFT | SWT.COMMAND)) != 0; startGesture(ControlPoint.create(mCanvas, e), new MarqueeGesture(mCanvas, toggle), mLastStateMask); @@ -575,6 +578,42 @@ public class GestureManager { // Otherwise, the drag means you are moving something mCanvas.showInvisibleViews(true); startGesture(ControlPoint.create(mCanvas, e), new MoveGesture(mCanvas), 0); + + // Render drag-images: Copy portions of the full screen render. + Image image = mCanvas.getImageOverlay().getImage(); + if (image != null) { + /** + * Transparency of the dragged image ([0-255]). We're using 30% + * translucency to make the image faint and not obscure the drag + * feedback below it. + */ + final byte DRAG_TRANSPARENCY = (byte) (0.3 * 255); + + List<Rectangle> rectangles = new ArrayList<Rectangle>(imageCount); + if (imageCount > 0) { + ImageData data = image.getImageData(); + Rectangle imageRectangle = new Rectangle(0, 0, data.width, data.height); + for (CanvasSelection item : mDragSelection) { + Rectangle bounds = item.getRect(); + // Some bounds can be outside the rendered rectangle (for + // example, in an absolute layout, you can have negative + // coordinates), so create the intersection of these bounds. + Rectangle clippedBounds = imageRectangle.intersection(bounds); + rectangles.add(clippedBounds); + } + Rectangle boundingBox = ImageUtils.getBoundingRectangle(rectangles); + double scale = mCanvas.getHorizontalTransform().getScale(); + e.image = SwtUtils.drawRectangles(image, rectangles, boundingBox, scale, + DRAG_TRANSPARENCY); + + // Set the image offset such that we preserve the relative + // distance between the mouse pointer and the top left corner of + // the dragged view + int deltaX = (int) (scale * (boundingBox.x - p.x)); + int deltaY = (int) (scale * (boundingBox.y - p.y)); + SwtUtils.setDragImageOffsets(e, -deltaX, -deltaY); + } + } } // No hover during drag (since no mouse over events are delivered diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java index 22a1a88..0243764 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java @@ -391,9 +391,20 @@ public class GraphicalEditorPart extends EditorPart double s = mCanvasViewer.getCanvas().getScale(); if (direction > 0) { - s = s * 2; + s = s * 1.2; } else { - s = s / 2; + s = s / 1.2; + } + + // Some operations are faster if the zoom is EXACTLY 1.0 rather than ALMOST 1.0. + // (This is because there is a fast-path when image copying and the scale is 1.0; + // in that case it does not have to do any scaling). + // + // If you zoom out 10 times and then back in 10 times, small rounding errors mean + // that you end up with a scale=1.0000000000000004. In the cases, when you get close + // to 1.0, just make the zoom an exact 1.0. + if (Math.abs(s-1.0) < 0.0001) { + s = 1.0; } mCanvasViewer.getCanvas().setScale(s, true /*redraw*/); @@ -1666,16 +1677,9 @@ public class GraphicalEditorPart extends EditorPart boolean isFrameworkResource = false; if (colon != -1) { // The URL contains a package name. - - // FIXME: We should consult the package and Do The Right Thing. - // If the package is @android (which is by far the most common case), - // then maybe we can look up the corresponding file in the "data/" folder - // in the SDK. - // Otherwise, the package MAY be the same package as the current project, - // in which case we can just ignore it (because it will be exactly - // relative to the current project's folder), and otherwise we may - // have to look in other projects. Fortunately, this is not common. - + // While the url format technically allows other package names, + // the platform apparently only supports @android for now (or if it does, + // there are no usages in the current code base so this is not common). String packageName = url.substring(typeBegin, colon); if ("android".equals(packageName)) { //$NON-NLS-1$ isFrameworkResource = true; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java index 337e76e..b920607 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java @@ -90,6 +90,15 @@ public class ImageOverlay extends Overlay { return mImage; } + /** + * Returns the currently painted image, or null if none has been set + * + * @return the currently painted image or null + */ + public Image getImage() { + return mImage; + } + @Override public void paint(GC gc) { if (mImage != null) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java index 21ad07c..f83d4a5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java @@ -17,10 +17,14 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.common.api.Rect; +import org.eclipse.swt.graphics.Rectangle; + import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; +import java.util.Iterator; +import java.util.List; /** * Utilities related to image processing. @@ -323,4 +327,28 @@ public class ImageUtils { return image; } + + /** + * Returns a bounding rectangle for the given list of rectangles. If the list is + * empty, the bounding rectangle is null. + * + * @param items the list of rectangles to compute a bounding rectangle for (may not be + * null) + * @return a bounding rectangle of the passed in rectangles, or null if the list is + * empty + */ + public static Rectangle getBoundingRectangle(List<Rectangle> items) { + Iterator<Rectangle> iterator = items.iterator(); + if (!iterator.hasNext()) { + return null; + } + + Rectangle bounds = iterator.next(); + Rectangle union = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height); + while (iterator.hasNext()) { + union.add(iterator.next()); + } + + return union; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java index e5113f8..8f5f048 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java @@ -396,6 +396,15 @@ class LayoutCanvas extends Canvas { } /** + * Returns the current {@link ImageOverlay} painting the rendered result + * + * @return the image overlay responsible for painting the rendered result, never null + */ + /* package */ ImageOverlay getImageOverlay() { + return mImageOverlay; + } + + /** * Returns the horizontal {@link ScaleInfo} transform object, which can map * a layout point into a control point. * diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java index 2b01b1c..142d8eb 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java @@ -72,7 +72,6 @@ import org.w3c.dom.Element; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.StringWriter; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -630,22 +629,10 @@ public class PaletteComposite extends Composite { // Shift the drag feedback image up such that it's centered under the // mouse pointer - // Eclipse 3.4 does not support drag image offsets - // TODO: Replace by direct field access when we drop Eclipse 3.4 support. - try { - Field xField = event.getClass().getDeclaredField("offsetX"); //$NON-NLS-1$ - Field yField = event.getClass().getDeclaredField("offsetY"); //$NON-NLS-1$ - - Rectangle imageBounds = mImage.getBounds(); - int offsetX = imageBounds.width / 2; - int offsetY = imageBounds.height / 2; - xField.set(event, Integer.valueOf(offsetX)); - yField.set(event, Integer.valueOf(offsetY)); - } catch (SecurityException e) { - } catch (NoSuchFieldException e) { - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - } + Rectangle imageBounds = mImage.getBounds(); + int offsetX = imageBounds.width / 2; + int offsetY = imageBounds.height / 2; + SwtUtils.setDragImageOffsets(event, offsetX, offsetY); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java index 3b85a3a..136414e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java @@ -361,6 +361,12 @@ public class SelectionManager implements ISelectionProvider { // reset alternate selection if any mAltSelection = null; + if (vi == null) { + // The user clicked outside the bounds of the root element; in that case, just + // select the root element. + vi = mCanvas.getViewHierarchy().getRoot(); + } + boolean redoLayout = hasExplodedItems(); // reset (multi)selection if any diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java index 88f91a0..66c3817 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java @@ -16,7 +16,9 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.common.api.Rect; +import com.sun.tools.javac.util.Pair; +import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; @@ -26,6 +28,8 @@ import org.eclipse.swt.widgets.Display; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.Raster; +import java.lang.reflect.Field; +import java.util.List; /** * Various generic SWT utilities such as image conversion. @@ -36,9 +40,10 @@ public class SwtUtils { } /** - * Returns the {@link PaletteData} describing the ARGB ordering expected from - * integers representing pixels for AWT {@link BufferedImage}. + * Returns the {@link PaletteData} describing the ARGB ordering expected from integers + * representing pixels for AWT {@link BufferedImage}. * + * @param imageType the {@link BufferedImage#getType()} type * @return A new {@link PaletteData} suitable for AWT images. */ public static PaletteData getAwtPaletteData(int imageType) { @@ -168,6 +173,138 @@ public class SwtUtils { } /** + * Sets the DragSourceEvent's offsetX and offsetY fields. + * + * @param event the {@link DragSourceEvent} + * @param offsetX the offset X value + * @param offsetY the offset Y value + */ + public static void setDragImageOffsets(DragSourceEvent event, int offsetX, int offsetY) { + // Eclipse 3.4 does not support drag image offsets + // event.offsetX = offsetX; + // event.offsetY= offsetY; + // FIXME: Replace by direct field access when we drop Eclipse 3.4 support. + try { + Class<DragSourceEvent> clz = DragSourceEvent.class; + Field xField = clz.getDeclaredField("offsetX"); //$NON-NLS-1$ + Field yField = clz.getDeclaredField("offsetY"); //$NON-NLS-1$ + xField.set(event, Integer.valueOf(offsetX)); + yField.set(event, Integer.valueOf(offsetY)); + } catch (SecurityException e) { + } catch (NoSuchFieldException e) { + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + } + + /** + * Returns the DragSourceEvent's offsetX and offsetY fields. + * + * @param event the {@link DragSourceEvent} + * @return A pair of the offset X and Y values, or null if it fails (e.g. on Eclipse + * 3.4) + */ + public static Pair<Integer,Integer> getDragImageOffsets(DragSourceEvent event) { + // Eclipse 3.4 does not support drag image offsets: + // return Pair.of(event.offsetX, event.offsetY); + // FIXME: Replace by direct field access when we drop Eclipse 3.4 support. + try { + Class<DragSourceEvent> clz = DragSourceEvent.class; + Field xField = clz.getDeclaredField("offsetX"); //$NON-NLS-1$ + Field yField = clz.getDeclaredField("offsetY"); //$NON-NLS-1$ + int offsetX = xField.getInt(event); + int offsetY = yField.getInt(event); + return Pair.of(offsetX, offsetY); + } catch (SecurityException e) { + } catch (NoSuchFieldException e) { + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + + return null; + } + + /** + * Creates a new image from a source image where the contents from a given set of + * bounding boxes are copied into the new image and the rest is left transparent. A + * scale can be applied to make the resulting image larger or smaller than the source + * image. Note that the alpha channel in the original image is ignored, and the alpha + * values for the painted rectangles will be set to a specific value passed into this + * function. + * + * @param image the source image + * @param rectangles the set of rectangles (bounding boxes) to copy from the source + * image + * @param boundingBox the bounding rectangle of the rectangle list, which can be + * computed by {@link ImageUtils#getBoundingRectangle} + * @param scale a scale factor to apply to the result, e.g. 0.5 to shrink the + * destination down 50%, 1.0 to leave it alone and 2.0 to zoom in by + * doubling the image size + * @param alpha the alpha (in the range 0-255) that painted bits should be set to + * @return a pair of the rendered cropped image, and the location within the source + * image that the crop begins (multiplied by the scale). May return null if + * there are no selected items. + */ + public static Image drawRectangles(Image image, + List<Rectangle> rectangles, Rectangle boundingBox, double scale, byte alpha) { + + if (rectangles.size() == 0 || boundingBox == null || boundingBox.isEmpty()) { + return null; + } + + ImageData srcData = image.getImageData(); + int destWidth = (int) (scale * boundingBox.width); + int destHeight = (int) (scale * boundingBox.height); + + ImageData destData = new ImageData(destWidth, destHeight, srcData.depth, srcData.palette); + byte[] alphaData = new byte[destHeight * destWidth]; + destData.alphaData = alphaData; + + for (Rectangle bounds : rectangles) { + int dx1 = bounds.x - boundingBox.x; + int dy1 = bounds.y - boundingBox.y; + int dx2 = dx1 + bounds.width; + int dy2 = dy1 + bounds.height; + + dx1 *= scale; + dy1 *= scale; + dx2 *= scale; + dy2 *= scale; + + int sx1 = bounds.x; + int sy1 = bounds.y; + int sx2 = sx1 + bounds.width; + int sy2 = sy1 + bounds.height; + + if (scale == 1.0d) { + for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy++) { + for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx++) { + destData.setPixel(dx, dy, srcData.getPixel(sx, sy)); + alphaData[dy * destWidth + dx] = alpha; + } + } + } else { + // Scaled copy. + int sxDelta = sx2 - sx1; + int dxDelta = dx2 - dx1; + int syDelta = sy2 - sy1; + int dyDelta = dy2 - dy1; + for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy = (dy - dy1) * syDelta / dyDelta + + sy1) { + for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx = (dx - dx1) * sxDelta + / dxDelta + sx1) { + assert sx < sx2 && sy < sy2; + destData.setPixel(dx, dy, srcData.getPixel(sx, sy)); + alphaData[dy * destWidth + dx] = alpha; + } + } + } + } + + return new Image(image.getDevice(), destData); + } + + /** * Converts the given SWT {@link Rectangle} into an ADT {@link Rect} * * @param swtRect the SWT {@link Rectangle} |