aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2010-11-19 11:29:34 -0800
committerTor Norbye <tnorbye@google.com>2010-11-28 19:31:17 -0800
commit6d7a354cc1ad968a2bd17cc4b8db5814490a0f1d (patch)
treebbdbfbf1b0f07573e0f1df973c286f167b5058a4 /eclipse/plugins/com.android.ide.eclipse.adt/src/com
parent13d8e868bfdae8a4ed15059144e210d9e995c606 (diff)
downloadsdk-6d7a354cc1ad968a2bd17cc4b8db5814490a0f1d.zip
sdk-6d7a354cc1ad968a2bd17cc4b8db5814490a0f1d.tar.gz
sdk-6d7a354cc1ad968a2bd17cc4b8db5814490a0f1d.tar.bz2
Show images during canvas drags
When you drag in the canvas, show a drag image for the view being dragged. The drag image is using 30% translucency such that it does not obscure the drag feedback below it. We're grabbing the image data right out of the rendered scene and just copying out the rectangles for the bounds of each selected item. This has to take the canvas zoom into effect too. While in the zoom code I modified the Zoom In and Zoom Out buttons to only zoom by a factor of 20% rather than 100%. I cleaned up the Pair class slightly; in an earlier version of this fix I was using it to pass back two results from the rectangle painting function; I don't do that anymore but the Pair might as well be cleaned up. (By the way, as suggested in review #19035 I rewrote the FIXME comment related to resource lookup and put it in this CL rather than revising the other one.) Change-Id: Iabe6e29c4073252f5046e3c26b56f4e7674a3f3a
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/Pair.java28
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasSelection.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java49
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtils.java28
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java9
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java21
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java141
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}