Wednesday, June 20, 2007

Flex native drag-n-drop an image from external applications

Adobe AIR provides some nice tools for interacting with the desktop; local database, filesystem, clipboard and native drag and drop. I have been working on a project which requires the ability to drag images from any source and drop them into a container. Should be a simple task. All of the examples like Christophe Coenraets SalesBuilder on Air and the sample apps from Adobe sure make it look easy enough, but none of them explore what I needed to do; they all drag items out of the container, but not in.

Now given AIR is still a beta release, I hope what I present below will not be needed in the future because this took a while to figure out. Maybe I missed something, so if anyone points out an easier way to do this, I'm all ears.

It seemed the obvious thing to do is something along the lines of:

var data:BitmapData = e.transferable.dataForFormat(TransferableFormats.BITMAP_FORMAT) as BitmapData;
myCanvas.addChild(data.content);

Burn! The bitmapData object appears to have data; it has height, width and rectangle properties, the size value indicates there's data in there, but when attempting to add the image to myCanvas, addChild reports an error of 'child must be non null'. I just couldn't reach in and get at the bits.

This led me down a rosy path of trying just about everything I could think to get that pretty picture I wanted to drag in. I ended up with the following. It basically serializes the transfer object's data to file, then I simply pick it back up as an image (note there are no format checks or validation in this example... study purposes only).

Note that you will need the PNGEncoder class, adopted by Adobe from Tinic Uro. These are working versions for Flex 2 and 3.

JPGEncoder.as (text)
PNGEnc (text)

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
applicationComplete="init()" layout="absolute">

<mx:Script>
<![CDATA[
import flash.filesystem.*;
import flash.geom.Matrix;
import flash.display.IBitmapDrawable;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.desktop.*;
import flash.events.NativeDragEvent;
import mx.controls.Image;

public function init():void {
myCanvas.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, nativeDragEnter);
myCanvas.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, nativeDragDrop);
}

public function nativeDragEnter(e:NativeDragEvent):void {
if (DragManager.dragInitiator == myCanvas) return; // If your dragging over yourself, ignore
DragManager.acceptDragDrop(myCanvas);
}

public function nativeDragDrop(e:NativeDragEvent):void {
var data:BitmapData = e.transferable.dataForFormat(TransferableFormats.BITMAP_FORMAT) as BitmapData;
var img:Image = encodeImage(data);
myCanvas.addChild(img);
}

private function encodeImage(data:BitmapData):Image {
// write transfer object's data to file
var stream:FileStream = new FileStream();
var matrix:Matrix = new Matrix();
matrix.translate( 0 - ( data.rect.x + 1 ), 0 - ( data.rect.y + 1 ) );
data.draw( data, matrix );

var png:* = null;
png = PNGEncoder.encode(data);

var file:File = File.applicationResourceDirectory.resolve( "tmp.png" );
stream.open( file, FileMode.WRITE );
stream.writeBytes( png, 0, 0 );
stream.close();

// now we can load the image back in
var img:Image = new Image();
img.load(file.nativePath);
return img;
}


]]>
</mx:Script>

<mx:Canvas id="myCanvas" width"400" height="300" backgroundColor="#FFFFFF">
</mx:Canvas>

</mx:WindowedApplication>

Now, with the added call in the nativeDragDrop() function, we have what we need:

var data:BitmapData = e.transferable.dataForFormat(TransferableFormats.BITMAP_FORMAT) as BitmapData;
var img:Image = encodeImage(data);
myCanvas.addChild(img);

Simple? Not exactly. Obvious? Certainly not! But, it shows that Flex can do quite a lot, with a little elbow grease. I am looking forward to the next AIR release, which should fix a number of other bugs and missives that will make our apps really fly.
Monday, June 11, 2007

Already widgetized

No sooner do I give the code to embed a Sprout, Jeff puts one up on his site and gives a full-blown review. Thanks man!

You should get over to http://www.edutechie.com and find out how one dedicated person is actually doing something for education. Jeff is a professional student... er... lifelong student who didn’t want to leave education, so he got a job at a university working in instructional technology and supports six large departments on campus which include numerous smaller organizations. And yet he still has time to write up some fascinating and thought-provoking articles.
Saturday, June 09, 2007

Sprouts as a Widget?

You can now take a Sprouts tree and transplant it out into the wild:



I have implemented this feature in the Publish option. A popup with generated embed code lets you copy to the system clipboard, which you can then paste into wherever you want.

Big shout out to Jeff V. for prodding me to make Sprouts available as a widget - great idea Jeff!