Showing posts with label app. Show all posts
Showing posts with label app. Show all posts

Tuesday, January 27, 2015

KirSizer Flex AIR Image Sizer app Part 3

Today well add some "Add files" button functionality.

Go to the first NavigatorContent container and ste the first labels id to labelSelected. Set the TileLists widht to 282, columnWidth and columnHeight to 60, columnCount to 4 and itemRenderer to a custom class called TileRenderer.

Set the "Add files" buttons click event handler to addFiles() function:

<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Label id="labelSelected">0 files selected</s:Label>
<mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" />
<s:Button label="Add folder" width="100%" />
<s:Button label="Add files" width="100%" click="addFiles();" />
<s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" />
</s:VGroup>
</s:NavigatorContent>

In the script tags declare a new ArrayCollection called slectedFiles, make it bindable:

[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);

Now create this addFiles() function and inside of it create a new File object. Call its browseForOpenMultiple() method and add a filter for jpg, jpeg and png pictures. Add a SELECT_MULTIPLE event listener for the File.

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

The fileSelected function, which is a handler to the SELECT_MULTIPLE event, loops through the receives files and adds their native paths to the selectedFiles array. However, we must keep in mind that the user might accidently import the same file twice, so we need to check if the file already exists in the array before adding it. We use another loop for that. After the loop, we set labelSelecteds text to display how many files are selected:

private function filesSelected(evt:FileListEvent):void {
for (var i:int = 0; i < evt.files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u < selectedFiles.length; u++) {
if (selectedFiles[u] == evt.files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem(evt.files[i].nativePath);
}
labelSelected.text = selectedFiles.length + " files selected";
}

Now create a new mxml file called TileRenderer.mxml. This is our custom item renderer for the TileList. Set the root tags to a VBox, and inside of it create an Image object with source bound to {data}, which is the received image url. Set maxWidth to 60, maxHeight to 60 and scaleContent to true:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
horizontalAlign="center"
verticalAlign="middle" horizontalScrollPolicy="off"
verticalScrollPolicy="off">
<mx:Image source="{data}" maxWidth="60" maxHeight="60" scaleContent="true"/>
</mx:VBox>

And full Main.mxml code looks like this:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460"
showStatusBar="false" title="KirSizer">

<fx:Declarations>
<mx:ArrayCollection id="measures">
<fx:String>%</fx:String>
<fx:String>px</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="actions">
<fx:String>Fixed width, fixed height</fx:String>
<fx:String>Fixed width, proportional height</fx:String>
<fx:String>Proportional width, fixed height</fx:String>
<fx:String>Proportional sizes to fit specified sizes</fx:String>
</mx:ArrayCollection>
<mx:ArrayCollection id="formats">
<fx:String>Same format as initial file</fx:String>
<fx:String>Convert all to JPG</fx:String>
<fx:String>Convert all to PNG</fx:String>
</mx:ArrayCollection>
<mx:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="300"/>
<mx:Fade id="fadeOut" alphaFrom="1" alphaTo="0" duration="300"/>
</fx:Declarations>

<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

#contentStack{
backgroundColor: #313131;
}

s|Label{
color: #fcfcfc;
}

s|Button{
chromeColor: #636363;
}

mx|ComboBox{
chromeColor: #636363;
color: #fcfcfc;
contentBackgroundColor: #000000;
rollOverColor: #aaaaaa;
selectionColor: #ffffff;
}
</fx:Style>

<fx:Script>
<![CDATA[
import flash.events.FileListEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import mx.collections.ArrayCollection;
import mx.effects.easing.Linear;
import mx.controls.Alert;

[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);

private function actionChange():void{
switch (actionCombo.selectedIndex) {
case 0: case 3:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 1:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = false;
heightMeasure.enabled = false;
break;
case 2:
newWidth.enabled = false;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
}
}

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

private function filesSelected(evt:FileListEvent):void {
for (var i:int = 0; i < evt.files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u < selectedFiles.length; u++) {
if (selectedFiles[u] == evt.files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem(evt.files[i].nativePath);
}
labelSelected.text = selectedFiles.length + " files selected";
}
]]>
</fx:Script>

<mx:ViewStack id="contentStack" width="100%" height="100%">
<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Label id="labelSelected">0 files selected</s:Label>
<mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" />
<s:Button label="Add folder" width="100%" />
<s:Button label="Add files" width="100%" click="addFiles();" />
<s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn">
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10">
<s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" />

<s:Label>Resize options:</s:Label>

<mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>
<s:HGroup verticalAlign="middle">
<s:Label width="50">Width:</s:Label>
<s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" />
<mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>
</s:HGroup>

<s:HGroup verticalAlign="middle">
<s:Label width="50">Height:</s:Label>
<s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/>
<mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>
</s:HGroup>

<s:Label/>

<s:Label>Output file names:</s:Label>
<s:HGroup verticalAlign="middle">
<s:TextInput width="240" text="%initialName%" />
<s:Button width="35" label="?"/>
</s:HGroup>

<s:Label/>

<s:Label>Output destination:</s:Label>
<s:HGroup verticalAlign="middle">
<s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" />
<s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" />
</s:HGroup>
<s:HGroup verticalAlign="middle" width="100%">
<s:TextInput width="100%" enabled="{newDestination.selected}" text="Select destination..." editable="false" />
<s:Button width="80" label="Browse" enabled="{newDestination.selected}"/>
</s:HGroup>

<s:Label/>

<s:Label>Output format:</s:Label>
<mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/>

<s:Label/>

<s:Label>Output JPG quality:</s:Label>
<s:HSlider width="100%" minimum="1" maximum="100" value="100" />

<s:Label/>

<s:Button label="Resize" width="100%" />
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</s:WindowedApplication>

Thanks for reading!
Read more »

Thursday, January 22, 2015

Creating a Flex AIR Annotator app Part 47

In this tutorial we will fix some minor bugs, improve drag and drop functionality, and add an ability to draw joined zigzag shapes.

If you test the code from the previous tutorial, youll notice that if you dnot open the image title window before dragging something in, the preview image may be displayed incorrectly - it will probably have bad offsets and incorrect scaling.

It works fine if the image title window is opened before the drag and drop feature is used, though. Therefore, we can deduce that the bug happens because the window had no time to render properly before displaying the preview images. To fix that, well go to the listeners() function and add some lines to add and immediately remove the image window after the application was launched:

private function listeners():void {
addEventListener(MouseEvent.MOUSE_UP, drawUp);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
addEventListener(Event.ENTER_FRAME, everyFrame);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDrop);
PopUpManager.addPopUp(imageWindow, this, true);
PopUpManager.centerPopUp(imageWindow);
PopUpManager.removePopUp(imageWindow);
}

Right now we can drag and drop images into drawing area to import them as image objects. If the application displays the first main screen, though, the application should do nothing. Instead of ignoring this drag and drop operation, we will accept it but do different kind of handling - we will open that image as a new project instead.

Go to onDragIn() and remove the if statement:

private function onDragIn(evt:NativeDragEvent):void {
NativeDragManager.acceptDragDrop(this);
}

Now in onDrop(), add a conditional checking for stacks selectedIndex. If it is 1, call addDropImage. If it is 0, call importDropImage.

private function onDrop(evt:NativeDragEvent):void {
var files:Array = evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
var file:File = files[0];
switch (file.extension.toLowerCase()) {
case "jpg":
case "jpeg":
case "png":
case "gif":
if(stack.selectedIndex == 0){
importDropImage(file);
}
if(stack.selectedIndex == 1){
addDropImage(file);
}
break;
default:
Alert.show("Unsupported file format.");
}
}

The importDropImage() simply creates a loader object which loads the file using its url and calls the loadComplete function on COMPLETE event of the loader:

private function importDropImage(file:File):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load(new URLRequest(file.url));
}

Now, we will add an ability to draw zig zags in our application. Right now, it is possible to do that by manually adding lines and connecting them. Connecting them by hand may not always be very precise (most of the time it will not be perfect). So, since we already have lastX and lastY coordinates recorded by drawing operations such as line and arrow drawing, we can use them to start a new line from these coordinates for pixel perfect line joints.

The user will be able to hold control to indicate that he wants to draw a connected line or arrow, and as he presses his mouse to create a line or an arrow while holding control, he beginning of this shape will be set to lastX and lastY coordinates.

First well need to know when control is held.

Declare isCtrl variable:

private var isCtrl:Boolean = false;

Handle it in keyDown and keyUp functions:

private function keyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 16) {
isShift = true;
}
if (evt.keyCode == 17) {
isCtrl = true;
}
if (evt.ctrlKey && evt.keyCode == 90 && undo_canUndo && toolbarStack.selectedIndex == 2) doUndo();
if (evt.ctrlKey && evt.keyCode == 89 && undo_canRedo && toolbarStack.selectedIndex == 2) doRedo();
}

private function keyUp(evt:KeyboardEvent):void {
if (evt.keyCode == 16) {
isShift = false;
}
if (evt.keyCode == 17) {
isCtrl = false;
}
}

Now go to drawDown() function and add conditionals inside the line and arrow drawing if statements. These conditionals check is isCtrl is held and if lastX and lastY values exist. If so, set shape start coordinates to previous coordinates. Otherwise, set them to mouse coordinates.

private function drawDown():void {
// cropping
if(toolbarStack.selectedIndex == 4 && mouseY>60) crop_mouseDown(drawArea.mouseX, drawArea.mouseY);

// drawing
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 0) {
drawMode = "pen";
tempSprite.graphics.clear();
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 3) {
drawMode = "line";
if (isCtrl && lastX && lastY) {
tempSprite.graphics.moveTo(lastX, lastY);
anchorX = lastX;
anchorY = lastY;
}else {
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 2) {
drawMode = "rect";
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 1) {
drawMode = "ellipse";
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 4) {
drawMode = "arrow";
if (isCtrl && lastX && lastY) {
tempSprite.graphics.moveTo(lastX, lastY);
anchorX = lastX;
anchorY = lastY;
}else {
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 5) {
drawMode = "picker";
drawTempBitmap();
getPickerColor();
}
}

Done!

Full code:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:custom="*"
showStatusBar="false"
creationComplete="init();"
applicationComplete="listeners();"
minWidth="800" minHeight="400"
width="800" height="500">

<fx:Script>
<![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.FocusDirection;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.utils.ByteArray;
import mx.containers.Canvas;
import mx.containers.TitleWindow;
import mx.controls.Alert;
import flash.filesystem.FileMode;
import mx.controls.Image;
import mx.controls.Label;
import mx.controls.TextArea;
import mx.core.FlexGlobals;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import spark.components.Group;
import spark.core.SpriteVisualElement;
import spark.primitives.Rect;
import flash.text.TextFieldAutoSize;
import mx.managers.PopUpManager;
import mx.utils.ObjectUtil;
import flash.display.BlendMode;
import flash.system.System;
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import flash.events.NativeDragEvent;
import flash.desktop.NativeDragManager;

private var bitmapData:BitmapData;
private var content:*;
[Bindable]
private var buttonsEnabled:Boolean = false;
[Bindable]
private var lineThickness:int = 5;
[Bindable]
private var lineColor:uint = 0xff0000;
[Bindable]
private var aBodyColor:uint = 0xffff88;
[Bindable]
private var aBodyAlpha:Number = 1;
[Bindable]
private var aTextSize:Number = 12;
[Bindable]
private var aTextColor:uint = 0x000000;

[Embed(source="../lib/cursor_cross.png")]
private var crossCursor:Class;
[Embed(source="../lib/cursor_move.png")]
private var moveCursor:Class;
[Embed(source="../lib/ic_eyedropper.png")]
private var pickerCursor:Class;
[Embed(source="../lib/cursor_resize_vertical.png")]
private var resizeVerticalCursor:Class;
[Embed(source="../lib/cursor_resize_horizontal.png")]
private var resizeHorizontalCursor:Class;
[Embed(source="../lib/cursor_resize_diag1.png")]
private var resizeDiag1Cursor:Class;
[Embed(source="../lib/cursor_resize_diag2.png")]
private var resizeDiag2Cursor:Class;
[Embed(source="../lib/cursor_empty.png")]
private var emptyCursor:Class;

private var drawMode:String = "";
private var anchorX:Number;
private var anchorY:Number;
private var lastX:Number;
private var lastY:Number;
private var isShift:Boolean = false;
private var isCtrl:Boolean = false;
private var temporaryBitmap:BitmapData;
private var currentlyEditing:*;
private var currentlyDragging:*;
private var dragOffsetX:Number;
private var dragOffsetY:Number;
private var currentImage:*;
private var imageAnchors:Array;
private var currentRatio:Number;
private var firstTimeEditing:Boolean = true;
private var currentAnn:*;
[Bindable]
private var screenshotAvailable:Boolean = false;
private var checkScreenshot:Boolean = true;

private var preferences:SharedObject = SharedObject.getLocal("kirannotatorPreferences");
private var selectedFormat:String;
private var selectedQuality:int;

private var img_resizing:Boolean = false;
private var img_currentDir:String = "";
private var img_anchorX:Number;
private var img_anchorY:Number;
private var img_rectangle:Array;

private var crop_rectangle:Array;
private var crop_anchorX:Number;
private var crop_anchorY:Number;
private var crop_drawing:Boolean = false;
private var crop_selectionType:Boolean;
private var crop_inSelection:Boolean = false;
private var crop_offsetX:Number;
private var crop_offsetY:Number;
private var crop_dragging:Boolean = false;
private var crop_inResize:Boolean = false;
private var crop_resizing:Boolean = false;
private var crop_resizeDir:String = "";
private var crop_resizeRadius:Number = 6;

[Bindable]
private var undo_canUndo:Boolean = false;
[Bindable]
private var undo_canRedo:Boolean = false;
private var undo_memory:Array;
private var undo_currentMemory:int;
private var undo_memoryLimit:int = 4;

private var annAnchors:Array;
private var ann_currentDir:String = "";
private var ann_anchorX:Number;
private var ann_resizing:Boolean;
private var ann_positions:Array;

private var erasing:Boolean = false;

private function init():void {
/* Uncomment this line to reset locally saved preferences: */
//preferences.data.firsttime = null;

// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.selectedFormat = jpg;
preferences.data.selectedQuality = 70;
preferences.data.selectionType = true;
preferences.flush();
}

// Set preferences loaded from local storage
selectedFormat = preferences.data.selectedFormat;
selectedQuality = preferences.data.selectedQuality;
crop_selectionType = preferences.data.selectionType;

drawArea.filters = [new DropShadowFilter(4, 60, 0, 0.7, 10, 10, 1, 3)];
eraser.filters = [new GlowFilter(0xffffff, 1, 2, 2, 10, 1)];
}

private function listeners():void {
addEventListener(MouseEvent.MOUSE_UP, drawUp);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
addEventListener(Event.ENTER_FRAME, everyFrame);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragIn);
this.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDrop);
PopUpManager.addPopUp(imageWindow, this, true);
PopUpManager.centerPopUp(imageWindow);
PopUpManager.removePopUp(imageWindow);
}

private function onDragIn(evt:NativeDragEvent):void {
NativeDragManager.acceptDragDrop(this);
}

private function onDrop(evt:NativeDragEvent):void {
var files:Array = evt.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
var file:File = files[0];
switch (file.extension.toLowerCase()) {
case "jpg":
case "jpeg":
case "png":
case "gif":
if(stack.selectedIndex == 0){
importDropImage(file);
}
if(stack.selectedIndex == 1){
addDropImage(file);
}
break;
default:
Alert.show("Unsupported file format.");
}
}

private function addDropImage(file:File):void {
toolbarStack.selectedIndex = 1;
openImage();
imageWindow.validateNow();

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComp);
loader.load(new URLRequest(file.url));

function loadComp(evt:Event):void {
iPreview.source = evt.target.content;
iPreviewHidden.source = evt.target.content;
iPath.text = file.nativePath;
updatePreviewImage();
}
}

private function importDropImage(file:File):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load(new URLRequest(file.url));
}

private function everyFrame(evt:Event):void{
if (checkScreenshot) {
var hasData:Boolean = Clipboard.generalClipboard.hasFormat(ClipboardFormats.BITMAP_FORMAT);
screenshotAvailable = hasData;
}
}

private function keyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 16) {
isShift = true;
}
if (evt.keyCode == 17) {
isCtrl = true;
}
if (evt.ctrlKey && evt.keyCode == 90 && undo_canUndo && toolbarStack.selectedIndex == 2) doUndo();
if (evt.ctrlKey && evt.keyCode == 89 && undo_canRedo && toolbarStack.selectedIndex == 2) doRedo();
}

private function keyUp(evt:KeyboardEvent):void {
if (evt.keyCode == 16) {
isShift = false;
}
if (evt.keyCode == 17) {
isCtrl = false;
}
}

private function canvasOver():void {
if ((toolbarStack.selectedIndex == 3 || toolbarStack.selectedIndex == 1) && currentlyDragging != null) {
cursorManager.setCursor(moveCursor, 2, -9, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools) {
if (drawTools.selectedIndex < 5) cursorManager.setCursor(crossCursor, 2, -10, -10);
if (drawMode == "pen" || drawMode == "line" || drawMode == "arrow") {
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (drawTools.selectedIndex == 5) cursorManager.setCursor(pickerCursor, 2, 0, -18);
if (drawTools.selectedIndex == 6) {
cursorManager.setCursor(emptyCursor, 2, -10, -10);
eraser.alpha = 1;
};
}
if (toolbarStack.selectedIndex == 4) {
if (!crop_dragging && !crop_resizing) cursorManager.setCursor(crossCursor, 2, -9, -10);
if (crop_dragging) cursorManager.setCursor(moveCursor, 2, -9, -10);
if (crop_resizing) {
switch(crop_resizeDir) {
case "up": case "down": cursorManager.setCursor(resizeVerticalCursor, 2, -9, -10); break;
case "right": case "left": cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10); break;
case "upLeft": case "downRight": cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10); break;
case "upRight": case "downLeft": cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10); break;
}
};
};
}

private function canvasOut():void {
eraser.alpha = 0;
cursorManager.removeAllCursors();
}

private function updateErase():void {
eraser.graphics.clear();
eraser.graphics.lineStyle(1, 0x000000);
eraser.graphics.drawCircle(canvas.mouseX + canvas.horizontalScrollPosition, canvas.mouseY + canvas.verticalScrollPosition, lineThickness*2);
}

private function moveErase():void {
updateErase();
if (erasing) doErase();
}

private function startErase():void {
if (toolbarStack.selectedIndex == 2 && drawTools && drawTools.selectedIndex == 6) {
erasing = true;
doErase();
}
}

private function doErase():void {
var hole:Group = new Group();
hole.graphics.beginFill(0x000000);
hole.graphics.drawCircle(0, 0, lineThickness * 2);
var holeMatrix:Matrix = new Matrix();
holeMatrix.translate(canvas.mouseX - drawArea.x + canvas.horizontalScrollPosition, canvas.mouseY - drawArea.y + canvas.verticalScrollPosition);
Bitmap(drawBitmap.content).bitmapData.draw(hole, holeMatrix, null, BlendMode.ERASE);
}

private function drawDown():void {
// cropping
if(toolbarStack.selectedIndex == 4 && mouseY>60) crop_mouseDown(drawArea.mouseX, drawArea.mouseY);

// drawing
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 0) {
drawMode = "pen";
tempSprite.graphics.clear();
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 3) {
drawMode = "line";
if (isCtrl && lastX && lastY) {
tempSprite.graphics.moveTo(lastX, lastY);
anchorX = lastX;
anchorY = lastY;
}else {
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 2) {
drawMode = "rect";
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 1) {
drawMode = "ellipse";
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 4) {
drawMode = "arrow";
if (isCtrl && lastX && lastY) {
tempSprite.graphics.moveTo(lastX, lastY);
anchorX = lastX;
anchorY = lastY;
}else {
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
cursorManager.removeAllCursors();
cursorManager.setCursor(emptyCursor, 2, -10, -10);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 5) {
drawMode = "picker";
drawTempBitmap();
getPickerColor();
}
}

private function drawUp(evt:MouseEvent):void {
if (erasing) {
erasing = false
var newBd:BitmapData = new BitmapData(content.width, content.height, true, 0xff0000);
newBd.draw(drawBitmap, null, null, null, null, true);
addUndo(newBd);
};
img_resizing = false;
ann_resizing = false;

// cropping
if (toolbarStack.selectedIndex == 4 && mouseY>60) crop_stopDraw();

// drawing
if (drawMode == "pen" || drawMode == "line" || drawMode == "rect" || drawMode == "ellipse" || drawMode == "arrow") {
doDrawBitmap();
tempSprite.graphics.clear();
cursorManager.removeAllCursors();
cursorManager.setCursor(crossCursor, 2, -9, -10);
}
drawMode = "";
currentlyDragging = null;
if (toolbarStack.selectedIndex == 3 || toolbarStack.selectedIndex == 1) {
drawHitArea.mouseEnabled = false;
cursorManager.removeAllCursors();
disableResize();
updateDrawHitArea();
}
}

private function addUndo(bd:BitmapData):void {
undo_canRedo = false;
if (undo_currentMemory < undo_memoryLimit - 1) {
undo_currentMemory++;
undo_memory[undo_currentMemory] = bd.clone();
undo_canUndo = true;
}else
if (undo_currentMemory == undo_memoryLimit - 1) {
undo_memory.shift();
undo_memory[undo_memoryLimit - 1] = bd.clone();
undo_canUndo = true;
}
}

private function doUndo():void {
undo_currentMemory--;
drawBitmap.source = new Bitmap(bdClone(undo_memory[undo_currentMemory]), "auto", true);
undo_canRedo = true;
if (undo_currentMemory > 0) undo_canUndo = true;
if (undo_currentMemory <= 0) undo_canUndo = false;
}

private function doRedo():void {
undo_currentMemory++;
drawBitmap.source = new Bitmap(bdClone(undo_memory[undo_currentMemory]), "auto", true);
undo_canUndo = true;
if (undo_currentMemory < undo_memory.length-1) undo_canRedo = true;
if (undo_currentMemory >= undo_memory.length-1) undo_canRedo = false;
}

private function bdClone(bd:BitmapData):BitmapData {
if (bd) {
return bd.clone();
}
else return new BitmapData(content.width, content.height, true, 0xff0000);
}

private function doDrawBitmap():void {
drawBitmap.width = content.width;
drawBitmap.height = content.height;
drawBitmap.validateNow();
var tempBd:BitmapData = new BitmapData(content.width, content.height, true, 0xff0000);
tempBd.draw(tempSprite, null, null, null, null, true);
var bd:BitmapData = new BitmapData(content.width, content.height, true, 0x00ff00);
bd.draw(drawBitmap, null, null, null, null, true);
bd.copyPixels(tempBd, new Rectangle(0, 0, content.width, content.height), new Point(0, 0), null, null, true);
drawBitmap.source = new Bitmap(bd, "auto", true);
addUndo(bd);
}

private function clearDrawBitmap():void {
undo_canRedo = false;
undo_canUndo = false;
undo_currentMemory = 0;
undo_memory = [];
drawBitmap.source = new Bitmap(new BitmapData(content.width, content.height, true, 0x00ffffff));
}

private function drawMove():void {
// debug
if (toolbarStack.selectedIndex == 2 && mouseY > 100 && focusManager.getFocus() != saveBtn) {
focusManager.setFocus(saveBtn);
}

// image resizing
imageResize();

// annotation resizing
annResize();

// cropping
if(toolbarStack.selectedIndex == 4) crop_moveDraw(drawArea.mouseX, drawArea.mouseY);

// drawing
if (drawMode == "pen") {
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.lineTo(drawArea.mouseX, drawArea.mouseY);
tempSprite.validateNow();
}
if (drawMode == "line") {
if (!isShift) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.moveTo(anchorX, anchorY);
tempSprite.graphics.lineTo(drawArea.mouseX, drawArea.mouseY);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (isShift) {
var straightLinePoint:Point = straightLine(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
if (straightLinePoint.x > lineThickness / 2 && straightLinePoint.x < drawArea.width - lineThickness / 2 &&
straightLinePoint.y > lineThickness/2 && straightLinePoint.y < drawArea.height - lineThickness /2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.moveTo(anchorX, anchorY);
tempSprite.graphics.lineTo(straightLinePoint.x, straightLinePoint.y);
lastX = straightLinePoint.x;
lastY = straightLinePoint.y;
}
}
}
if (drawMode == "rect") {
var newRectValues:Array;
if (!isShift) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newRectValues = calculateRectValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
if(isShift){
newRectValues = calculateSquareValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
tempSprite.graphics.drawRect(newRectValues[0], newRectValues[1], newRectValues[2], newRectValues[3]);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (drawMode == "ellipse") {
var newEllipseValues:Array;
if (!isShift) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newEllipseValues = calculateRectValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
if(isShift){
newEllipseValues = calculateSquareValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
tempSprite.graphics.drawEllipse(newEllipseValues[0], newEllipseValues[1], newEllipseValues[2], newEllipseValues[3]);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (drawMode == "arrow") {
var mouseX:Number = (isShift)?(straightLine(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY).x):(drawArea.mouseX);
var mouseY:Number = (isShift)?(straightLine(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY).y):(drawArea.mouseY);
var angle:Number = Math.atan2(anchorY - mouseY, anchorX - mouseX);
var armlength:Number = 10 + 2.5 * lineThickness;
var offset:Number = (24 - 0.5 * lineThickness) * Math.PI / 180;
var arm1x:Number = armlength * Math.cos(angle + offset) + mouseX;
var arm1y:Number = armlength * Math.sin(angle + offset) + mouseY;
var arm2x:Number = armlength * Math.cos(angle - offset) + mouseX;
var arm2y:Number = armlength * Math.sin(angle - offset) + mouseY;

if (arm1x > 0 + lineThickness / 2 && arm1x < drawArea.width - lineThickness / 2 &&
arm1y > 0 + lineThickness / 2 && arm1y < drawArea.height - lineThickness / 2 &&
arm2x > 0 + lineThickness / 2 && arm2x < drawArea.width - lineThickness / 2 &&
arm2y > 0 + lineThickness / 2 && arm2y < drawArea.height - lineThickness / 2 &&
mouseX > 9 + lineThickness / 2 && mouseX < drawArea.width - lineThickness / 2 - 9 &&
mouseY > 9 + lineThickness / 2 && mouseY < drawArea.height - lineThickness / 2 - 9){
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor, 1, false, "normal", CapsStyle.NONE, JointStyle.MITER);
tempSprite.graphics.moveTo(anchorX, anchorY);
tempSprite.graphics.lineTo(mouseX, mouseY);

tempSprite.graphics.beginFill(lineColor, 1);
tempSprite.graphics.moveTo(arm1x, arm1y);
tempSprite.graphics.lineTo(mouseX, mouseY);
tempSprite.graphics.lineTo(arm2x, arm2y);
tempSprite.graphics.lineTo(arm1x, arm1y);
tempSprite.graphics.endFill();

lastX = mouseX;
lastY = mouseY;
}
}
if (drawMode == "picker") {
getPickerColor();
}
if ((toolbarStack.selectedIndex == 3 || toolbarStack.selectedIndex == 1) && currentlyDragging != null) {
var nextDragX:Number = drawArea.mouseX - dragOffsetX;
var nextDragY:Number = drawArea.mouseY - dragOffsetY;
if (nextDragX >= 0 && nextDragX + currentlyDragging.width <= content.width) currentlyDragging.x = nextDragX;
if (nextDragY >= 0 && nextDragY + currentlyDragging.height <= content.height) currentlyDragging.y = nextDragY;
if (nextDragX < 0) currentlyDragging.x = 0;
if (nextDragY < 0) currentlyDragging.y = 0;
if (nextDragX + currentlyDragging.width > content.width) currentlyDragging.x = content.width - currentlyDragging.width;
if (nextDragY + currentlyDragging.height > content.height) currentlyDragging.y = content.height - currentlyDragging.height;
if (toolbarStack.selectedIndex == 1) positionResize();
if (toolbarStack.selectedIndex == 3) positionAnnResize();
}
}

private function straightLine(mouseX:Number, mouseY:Number, anchX:Number, anchY:Number):Point {
var p:Point = new Point(0, 0);
var rotation:Number = Math.atan2(mouseY - anchY, mouseX - anchX) * 180 / Math.PI;
if (rotation<-35 && rotation>-55) {
p = new Point(mouseX, anchY - (mouseX - anchX));
}else
if (rotation<-125 && rotation>-145) {
p = new Point(mouseX, (mouseX - anchX) + anchY);
}else
if (rotation>35 && rotation<55) {
p = new Point(mouseX, (mouseX - anchX) + anchY);
}else
if (rotation>125 && rotation<145) {
p = new Point(mouseX, anchY - (mouseX - anchX));
}else
if (Math.abs(mouseX - anchX) > Math.abs(mouseY - anchY)) {
p = new Point(mouseX, anchY);
}else
if (Math.abs(mouseX - anchX) < Math.abs(mouseY - anchY)) {
p = new Point(anchX, mouseY);
}
return p;
}

private function calculateRectValues(currentX:Number, currentY:Number, anchX:Number, anchY:Number):Array {
var newrect:Array = [0, 0, 0, 0];
// bottom - right
if (currentX > anchX && currentY > anchY) {
newrect[0] = anchX;
newrect[1] = anchY-1;
newrect[2] = currentX - anchX;
newrect[3] = currentY - anchY;
}
// bottom - left
if (currentX < anchX && currentY > anchY) {
newrect[0] = currentX;
newrect[1] = anchY-1;
newrect[2] = anchX - currentX;
newrect[3] = currentY - anchY;
}
// top - right
if (currentX > anchX && currentY < anchY) {
newrect[0] = anchX;
newrect[1] = currentY-1;
newrect[2] = currentX - anchX;
newrect[3] = anchY - currentY;
}
// top - left
if (currentX < anchX && currentY < anchY) {
newrect[0] = currentX;
newrect[1] = currentY-1;
newrect[2] = anchX - currentX;
newrect[3] = anchY - currentY;
}
return newrect;
}

private function calculateSquareValues(currentX:Number, currentY:Number, anchX:Number, anchY:Number):Array {
var newrect:Array = [0, 0, 0, 0];
// bottom - right
if (currentX > anchX && currentY > anchY) {
currentY = (currentX - anchX) + anchY;
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = anchX;
newrect[1] = anchY-1;
newrect[2] = currentX - anchX;
newrect[3] = currentY - anchY;
}
}
// bottom - left
if (currentX < anchX && currentY > anchY) {
currentY = anchY - (currentX - anchX);
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = currentX;
newrect[1] = anchY-1;
newrect[2] = anchX - currentX;
newrect[3] = currentY - anchY;
}
}
// top - right
if (currentX > anchX && currentY < anchY) {
currentY = anchY - (currentX - anchX);
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = anchX;
newrect[1] = currentY-1;
newrect[2] = currentX - anchX;
newrect[3] = anchY - currentY;
}
}
// top - left
if (currentX < anchX && currentY < anchY) {
currentY = (currentX - anchX) + anchY;
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = currentX;
newrect[1] = currentY-1;
newrect[2] = anchX - currentX;
newrect[3] = anchY - currentY;
}
}
return newrect;
}

private function importPicture():void {
var file:File = new File();
var imageFilter:FileFilter = new FileFilter("Images", "*.jpg;*jpeg;*.gif;*.png");
file.browseForOpen("Import picture", [imageFilter]);
file.addEventListener(Event.SELECT, fileSelect);
}

private function importScreenshot():void {
content = Clipboard.generalClipboard.getData(ClipboardFormats.BITMAP_FORMAT) as BitmapData;
bitmapData = Clipboard.generalClipboard.getData(ClipboardFormats.BITMAP_FORMAT) as BitmapData;
stackChange(); // needed if selectedIndex was already 1 when the function was called
stack.selectedIndex = 1;
}

private function fileSelect(evt:Event):void {
var file:File = evt.target as File;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load(new URLRequest(file.url));
}

private function loadComplete(evt:Event):void {
content = evt.target.content;
bitmapData = new BitmapData(content.width, content.height, false);
bitmapData.draw(content, new Matrix(), null, null, null, true);
stackChange(); // needed if selectedIndex was already 1 when the function was called
stack.selectedIndex = 1;
}

private function stackChange():void {
if (stack.selectedIndex == 1) {
imgHolder.source = new Bitmap(bitmapData);
drawArea.width = content.width;
drawArea.height = content.height;
drawGroup.width = content.width;
drawGroup.height = content.height;
cropDraw.width = content.width;
cropDraw.height = content.height;
eraser.width = content.width;
eraser.height = content.height;
buttonsEnabled = true;
clearDrawBitmap();
var anns:Number = annotations.numChildren;
for (var i:int = 0; i < anns; i++){
annotations.removeElementAt(0);
}
var imgs:Number = images.numChildren;
for (var u:int = 0; u < imgs; u++){
images.removeElementAt(0);
}
drawTempBitmap();
updateDrawHitArea();
crop_deselect();
undo_canRedo = false;
undo_canUndo = false;
undo_currentMemory = 0;
undo_memory = [];
}
}

private function toolbarChange():void {
if (toolbarStack.selectedIndex == 1) {
drawHitArea.mouseEnabled = false;
annotations.buttonMode = false;
annotations.mouseEnabled = false;
annotations.mouseChildren = false;
images.buttonMode = true;
images.mouseEnabled = true;
images.mouseChildren = true;
}else if
(toolbarStack.selectedIndex == 2) {
updateLineExample();
drawHitArea.mouseEnabled = true;
}else if
(toolbarStack.selectedIndex == 3) {
drawHitArea.mouseEnabled = false;
annotations.buttonMode = true;
annotations.mouseEnabled = true;
annotations.mouseChildren = true;
images.buttonMode = false;
images.mouseEnabled = false;
images.mouseChildren = false;
}else if
(toolbarStack.selectedIndex == 4) {
drawHitArea.mouseEnabled = true;
drawHitArea.width = content.width;
drawHitArea.height = content.height;
drawHitArea.x = -1;
drawHitArea.y = 1;
}else {
drawHitArea.mouseEnabled = false;
}
}

private function updateLineExample():void{
lineExample.graphics.clear();
lineExample.graphics.lineStyle(lineThickness, lineColor);
lineExample.graphics.moveTo(10, 10);
lineExample.graphics.lineTo(30, 10);

updateDrawHitArea();
}

private function updateDrawHitArea():void {
drawHitArea.width = content.width - lineThickness;
drawHitArea.height = content.height - lineThickness;
drawHitArea.x = lineThickness / 2;
drawHitArea.y = lineThickness / 2;

annHitArea.width = content.width;
annHitArea.height = content.height;
annHitArea.x = 0;
annHitArea.y = 0;
}

private function drawTempBitmap():void {
temporaryBitmap = new BitmapData(content.width, content.height, false, 0xffffff);
temporaryBitmap.draw(drawArea);
}

private function getPickerColor():void {
lineColor = temporaryBitmap.getPixel(drawArea.mouseX, drawArea.mouseY);
if (isShift) {
var myClip:Clipboard = Clipboard.generalClipboard;
myClip.setData(ClipboardFormats.TEXT_FORMAT, "#" + lineColor.toString(16));
}
}

private function openAnnotation(createX:Number = 0, createY:Number = 0):void {
if(toolbarStack.selectedIndex==3){
PopUpManager.addPopUp(annotationWindow, this, true);
PopUpManager.centerPopUp(annotationWindow);
annText.text = "Enter text here";
annotationWindow.title = "Create new annotation";
annWidth.maximum = drawArea.width;
if (firstTimeEditing) annWidth.value = 200;
firstTimeEditing = false;
annX.value = createX;
annY.value = createY;
updateAnnCoordLimits();
baNew.enabled = true;
baEdit.enabled = false;
baDelete.enabled = false;
}
}

private function closeAnnotation():void {
PopUpManager.removePopUp(annotationWindow);
}

private function addAnnotation():void {
var newAnn:spark.components.TextArea = new spark.components.TextArea();
newAnn.text = annText.text;
newAnn.width = annWidth.value;
newAnn.setStyle("contentBackgroundColor", aBodyColor);
newAnn.setStyle("color", aTextColor);
newAnn.setStyle("borderVisible", annBorder.selected);
newAnn.setStyle("contentBackgroundAlpha", aBodyAlpha)
newAnn.setStyle("fontSize", aTextSize);
newAnn.editable = false;
newAnn.selectable = false;
annotations.addElement(newAnn);
newAnn.validateNow();
newAnn.heightInLines = NaN;
newAnn.x = annX.value;
newAnn.y = annY.value;
newAnn.addEventListener(MouseEvent.ROLL_OVER, annOver);
newAnn.addEventListener(MouseEvent.ROLL_OUT, annOut);
newAnn.addEventListener(MouseEvent.DOUBLE_CLICK, annClick);
newAnn.addEventListener(MouseEvent.MOUSE_DOWN, annDown);
newAnn.addEventListener(MouseEvent.MOUSE_MOVE, annMove);
newAnn.buttonMode = true;
}

private function editAnnotation():void {
var editAnn:* = currentlyEditing;
editAnn.text = annText.text;
editAnn.width = annWidth.value;
editAnn.setStyle("contentBackgroundColor", aBodyColor);
editAnn.setStyle("borderVisible", annBorder.selected);
editAnn.setStyle("color", aTextColor);
editAnn.setStyle("contentBackgroundAlpha", aBodyAlpha);
editAnn.setStyle("fontSize", aTextSize);
editAnn.heightInLines = NaN;
editAnn.x = annX.value;
editAnn.y = annY.value;
}

private function deleteAnnotation():void {
annotations.removeElement(currentlyEditing);
}

private function annOver(evt:MouseEvent):void {
disableResize();
if (toolbarStack.selectedIndex == 3) {
evt.target.alpha = 0.8;
currentAnn = evt.currentTarget;
createAnnAnchors();
enableAnnResize();
positionAnnResize();
}
}

private function annOut(evt:MouseEvent):void{
evt.target.alpha = 1;
if (toolbarStack.selectedIndex == 3 && currentlyDragging == null) {
cursorManager.removeAllCursors();
}
}

private function annClick(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
currentlyEditing = evt.currentTarget;
PopUpManager.addPopUp(annotationWindow, this, true);
PopUpManager.centerPopUp(annotationWindow);
annText.text = currentlyEditing.text;
annotationWindow.title = "Edit annotation";
annWidth.maximum = drawArea.width;
annWidth.value = currentlyEditing.width;
aBodyColor = currentlyEditing.getStyle("contentBackgroundColor");
aTextColor = currentlyEditing.getStyle("color");
aBodyAlpha = currentlyEditing.getStyle("contentBackgroundAlpha");
aTextSize = currentlyEditing.getStyle("fontSize");
annBorder.selected = currentlyEditing.getStyle("borderVisible");
annX.value = currentlyEditing.x;
annY.value = currentlyEditing.y;
updateAnnCoordLimits();
baNew.enabled = false;
baEdit.enabled = true;
baDelete.enabled = true;
}
}

private function annDown(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
currentlyDragging = evt.currentTarget;
dragOffsetX = evt.currentTarget.mouseX;
dragOffsetY = evt.currentTarget.mouseY;
}
}

private function annMove(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3 && currentlyDragging && !drawHitArea.mouseEnabled) {
annotations.setElementIndex(currentlyDragging, annotations.numElements - 1);
cursorManager.setCursor(moveCursor, 2, -10, -10);
drawHitArea.mouseEnabled = true;
drawHitArea.x = -1;
drawHitArea.y = 0;
drawHitArea.width = content.width;
drawHitArea.height = content.height;
}
}

private function updateAnnCoordLimits():void{
annX.maximum = content.width - annWidth.value;
annY.maximum = content.height;
}

private function newFile():void {
PopUpManager.addPopUp(newfileWindow, this, true);
PopUpManager.centerPopUp(newfileWindow);
newfileStack.selectedIndex = 0;
newfileWindow.width = 200;
newfileWindow.height = 130;
checkScreenshot = true;
}

private function closeNewfile():void {
PopUpManager.removePopUp(newfileWindow);
checkScreenshot = false;
}

private function blankPicture():void {
content = new Image();
bitmapData = new BitmapData(newWidth.value, newHeight.value, false, newColor.selectedColor);
content.width = newWidth.value;
content.height = newHeight.value;
stackChange();
stack.selectedIndex = 1;
}

private function saveFile():void {
PopUpManager.addPopUp(saveWindow, this, true);
PopUpManager.centerPopUp(saveWindow);
if (selectedFormat == jpg) {
radioJPG.selected = true;
}
if (selectedFormat == png) {
radioPNG.selected = true;
}
sliderJPG.value = selectedQuality;
}

private function closeSave():void {
PopUpManager.removePopUp(saveWindow);
}

private function changeSelectedFormat(format:String):void {
selectedFormat = format;
preferences.data.selectedFormat = selectedFormat;
preferences.flush();
}

private function changeSelectedQuality():void {
selectedQuality = sliderJPG.value;
preferences.data.selectedQuality = selectedQuality;
preferences.flush();
}

private function startExport():void {
var encoder:IImageEncoder;
var byteArray:ByteArray;
var file:File;
var fileStream:FileStream;
var bd:BitmapData;

if (selectedFormat == "jpg") {
encoder = new JPEGEncoder(selectedQuality);
}
if (selectedFormat == "png") {
encoder = new PNGEncoder();
}

if(crop_rectangle[2]==0 || crop_rectangle[3]==0){
bd = new BitmapData(content.width, content.height, false);
bd.draw(drawArea);
}else {
cropDraw.visible = false;
cropDraw.validateNow();
bd = new BitmapData(crop_rectangle[2] + 1, crop_rectangle[3] + 1, false);
bd.draw(drawArea, new Matrix(1, 0, 0, 1, -crop_rectangle[0], -crop_rectangle[1]));
cropDraw.visible = true;
}

byteArray = encoder.encode(bd);
file = File.documentsDirectory.resolvePath("annotation");
file.browseForSave("Save annotated image");
file.addEventListener(Event.SELECT, saveSelect);

function saveSelect():void {
file.nativePath += "." + selectedFormat;
if(!file.exists){
fileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeBytes(byteArray);
fileStream.close();
} else {
Alert.show("File already exists. Overwrite?", "Hey!", Alert.YES | Alert.NO, null, onCloseOverwrite);
}
}

function onCloseOverwrite(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
fileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeBytes(byteArray);
fileStream.close();
}
}
}

private function crop_mouseDown(mouseX:Number, mouseY:Number):void {
if (!crop_inSelection && !crop_inResize) crop_startDraw(mouseX, mouseY);
if (crop_inSelection && !crop_inResize) crop_startDrag(mouseX - crop_rectangle[0], mouseY - crop_rectangle[1]);
if (crop_inResize) crop_startResize();
}

private function crop_startDraw(mouseX:Number, mouseY:Number):void {
crop_anchorX = mouseX;
crop_anchorY = mouseY;
crop_drawing = true;

crop_rectangle = [crop_anchorX, crop_anchorY, 0, 0];
crop_updateDraw();
}

private function crop_startDrag(offX:Number, offY:Number):void {
crop_offsetX = offX;
crop_offsetY = offY;
crop_dragging = true;
}

private function crop_startResize():void {
crop_resizing = true;
if (crop_resizeDir == "up" || crop_resizeDir == "upLeft" || crop_resizeDir == "upRight") crop_anchorY = crop_rectangle[1] + crop_rectangle[3];
if (crop_resizeDir == "down" || crop_resizeDir == "downLeft" || crop_resizeDir == "downRight") crop_anchorY = crop_rectangle[1];
if (crop_resizeDir == "left" || crop_resizeDir == "upLeft" || crop_resizeDir == "downLeft") crop_anchorX = crop_rectangle[0] + crop_rectangle[2];
if (crop_resizeDir == "right" || crop_resizeDir == "upRight" || crop_resizeDir == "downRight") crop_anchorX = crop_rectangle[0];
}

private function crop_stopDraw():void {
crop_drawing = false;
crop_dragging = false;
crop_resizing = false;
if (new Rectangle(drawArea.x, drawArea.y, content.width, content.height).containsPoint(new Point(canvas.mouseX, canvas.mouseY)) == false && mouseY>=canvas.y){
cursorManager.removeAllCursors();
cursorManager.setCursor(crossCursor, 2, -9, -10);
}
}

private function crop_areaRollOut():void {
if(!crop_drawing && !crop_dragging && !crop_resizing && toolbarStack.selectedIndex == 4){
cursorManager.removeAllCursors();
cursorManager.setCursor(crossCursor, 2, -9, -10);
}
}

private function crop_moveDraw(mouseX:Number, mouseY:Number):void {
if(!crop_drawing && !crop_resizing && !crop_dragging){
cursorManager.removeAllCursors();
crop_inSelection = new Rectangle(crop_rectangle[0], crop_rectangle[1], crop_rectangle[2], crop_rectangle[3]).containsPoint(new Point(mouseX, mouseY));
if (new Rectangle(crop_rectangle[0] - crop_resizeRadius, crop_rectangle[1] - crop_resizeRadius, crop_resizeRadius * 2, crop_resizeRadius * 2).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "upLeft";
crop_inResize = true;
cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0] + crop_rectangle[2] - crop_resizeRadius, crop_rectangle[1] - crop_resizeRadius, crop_resizeRadius * 2, crop_resizeRadius * 2).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "upRight";
crop_inResize = true;
cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0] - crop_resizeRadius, crop_rectangle[1] + crop_rectangle[3] - crop_resizeRadius, crop_resizeRadius * 2, crop_resizeRadius * 2).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "downLeft";
crop_inResize = true;
cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0] + crop_rectangle[2] - crop_resizeRadius, crop_rectangle[1] + crop_rectangle[3] - crop_resizeRadius, crop_resizeRadius * 2, crop_resizeRadius * 2).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "downRight";
crop_inResize = true;
cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0], crop_rectangle[1] - crop_resizeRadius, crop_rectangle[2], crop_resizeRadius * 2).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "up";
crop_inResize = true;
cursorManager.setCursor(resizeVerticalCursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0], crop_rectangle[1] + crop_rectangle[3] - crop_resizeRadius, crop_rectangle[2], crop_resizeRadius * 2).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "down";
crop_inResize = true;
cursorManager.setCursor(resizeVerticalCursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0] - crop_resizeRadius, crop_rectangle[1], crop_resizeRadius * 2, crop_rectangle[3]).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "left";
crop_inResize = true;
cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10)
}else
if (new Rectangle(crop_rectangle[0] + crop_rectangle[2] - crop_resizeRadius, crop_rectangle[1], crop_resizeRadius * 2, crop_rectangle[3]).containsPoint(new Point(mouseX, mouseY))) {
crop_resizeDir = "right";
crop_inResize = true;
cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10)
}else
{crop_inResize = false; }
if (crop_inSelection && !crop_inResize) cursorManager.setCursor(moveCursor, 2, -9, -10);
if (!crop_inSelection && !crop_inResize) cursorManager.setCursor(crossCursor, 2, -9, -10);
}
if (crop_drawing) {
crop_rectangle = calculateRectValues(mouseX, mouseY, crop_anchorX, crop_anchorY);
crop_updateDraw();
}
if (crop_dragging) {
var newX:Number = mouseX - crop_offsetX;
var newY:Number = mouseY - crop_offsetY;
if (newX >= 0 && newX + crop_rectangle[2] < content.width) crop_rectangle[0] = newX;
if (newX < 0) crop_rectangle[0] = 0;
if (newX + crop_rectangle[2] >= content.width) crop_rectangle[0] = content.width - crop_rectangle[2] - 1;
if (newY >= 0 && newY + crop_rectangle[3] < content.height) crop_rectangle[1] = newY;
if (newY < 0) crop_rectangle[1] = 0;
if (newY + crop_rectangle[3] >= content.height) crop_rectangle[1] = content.height - crop_rectangle[3] - 1;
crop_updateDraw();
}
if (crop_resizing) {
var prevRect:Array = crop_rectangle;
if (crop_resizeDir == "up" && mouseY >= 0 && mouseY < crop_anchorY) crop_rectangle = [prevRect[0], mouseY - 1, prevRect[2], crop_anchorY - mouseY + 1];
if (crop_resizeDir == "down" && mouseY < content.height && mouseY > crop_anchorY) crop_rectangle = [prevRect[0], crop_anchorY, prevRect[2], mouseY - crop_anchorY];
if (crop_resizeDir == "left" && mouseX >= 0 && mouseX < crop_anchorX) crop_rectangle = [mouseX, prevRect[1], crop_anchorX - mouseX, prevRect[3]];
if (crop_resizeDir == "right" && mouseX < content.width && mouseX > crop_anchorX) crop_rectangle = [crop_anchorX, prevRect[1], mouseX - crop_anchorX, prevRect[3]];
if (crop_resizeDir == "upLeft" && mouseY >= 0 && mouseY < crop_anchorY && mouseX >= 0 && mouseX < crop_anchorX) crop_rectangle = [mouseX, mouseY - 1, crop_anchorX - mouseX, crop_anchorY - mouseY + 1];
if (crop_resizeDir == "upRight" && mouseY >= 0 && mouseY < crop_anchorY && mouseX < content.width && mouseX > crop_anchorX) crop_rectangle = [crop_anchorX, mouseY - 1, mouseX - crop_anchorX, crop_anchorY - mouseY + 1];
if (crop_resizeDir == "downLeft" && mouseY < content.height && mouseY > crop_anchorY && mouseX >= 0 && mouseX < crop_anchorX) crop_rectangle = [mouseX, crop_anchorY, crop_anchorX - mouseX, mouseY - crop_anchorY];
if (crop_resizeDir == "downRight" && mouseY < content.height && mouseY > crop_anchorY && mouseX < content.width && mouseX > crop_anchorX) crop_rectangle = [crop_anchorX, crop_anchorY, mouseX - crop_anchorX, mouseY - crop_anchorY];
crop_updateDraw();
}
}

private function crop_updateDraw():void {
cropDraw.graphics.clear();
if(!crop_selectionType){
cropDraw.graphics.lineStyle(1, 0xff0000);
cropDraw.graphics.beginFill(0x00ff00, 0.2);
cropDraw.graphics.drawRect(crop_rectangle[0] - 1, crop_rectangle[1] - 1, crop_rectangle[2] + 2, crop_rectangle[3] + 2);
}
if(crop_selectionType){
cropDraw.graphics.beginFill(0x000000, 0.4);
cropDraw.graphics.lineStyle(1, 0x000000, 0);
cropDraw.graphics.drawRect(0, 0, content.width, crop_rectangle[1]);
cropDraw.graphics.drawRect(0, crop_rectangle[1], crop_rectangle[0], crop_rectangle[3] + 1);
cropDraw.graphics.drawRect(crop_rectangle[0] + crop_rectangle[2] + 1, crop_rectangle[1], content.width - crop_rectangle[0] - crop_rectangle[2] - 1, crop_rectangle[3] + 1);
cropDraw.graphics.drawRect(0, crop_rectangle[1] + crop_rectangle[3] + 1, content.width, content.height - crop_rectangle[1] - crop_rectangle[3] - 1);
}
if (crop_rectangle[2] == 0 || crop_rectangle[3] == 0) cropDraw.graphics.clear();
}

private function crop_deselect():void {
crop_rectangle = [0, -1000, 0, 0];
cropDraw.graphics.clear();
}

private function crop_changeSelectionType():void {
crop_selectionType = !crop_selectionType;
preferences.data.selectionType = crop_selectionType;
preferences.flush();
crop_updateDraw();
}

private function openImage():void {
if(toolbarStack.selectedIndex==1){
PopUpManager.addPopUp(imageWindow, this, true);
PopUpManager.centerPopUp(imageWindow);
imageWindow.title = "Add new image";
biNew.enabled = false;
biUpdate.enabled = false;
biDelete.enabled = false;
iPath.text = "";
iPreview.source = null;
iWidth.value = 10;
iHeight.value = 10;
iX.value = 0;
iY.value = 0;
updateImgLimits();
}
}

private function closeImage():void {
PopUpManager.removePopUp(imageWindow);
}

private function imageBrowse():void {
var file:File = new File();
var imageFilter:FileFilter = new FileFilter("Images", "*.jpg;*jpeg;*.gif;*.png");
file.browseForOpen("Import picture", [imageFilter]);
file.addEventListener(Event.SELECT, fileSel);

function fileSel(evt:Event):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComp);
loader.load(new URLRequest(file.url));
}

function loadComp(evt:Event):void {
iPreview.source = evt.target.content;
iPreviewHidden.source = evt.target.content;
iPath.text = file.nativePath;
updatePreviewImage();
}
}

private function updatePreviewImage():void {
var ratio:Number;
smoothImage(iPreview);
iPreviewHidden.validateNow();
iWidth.value = iPreviewHidden.contentWidth;
iHeight.value = iPreviewHidden.contentHeight;
if (iWidth.value > content.width) {
ratio = iHeight.value / iWidth.value;
iWidth.value = content.width;
iHeight.value = content.width * ratio;
}
if (iHeight.value > content.height) {
ratio = iWidth.value / iHeight.value;
iHeight.value = content.height;
iWidth.value = content.height * ratio;
}
imgOriginal.text = "Original size: " + iPreviewHidden.contentWidth + "x" + iPreviewHidden.contentHeight;
if (!biUpdate.enabled) biNew.enabled = true;
}

private function smoothImage(target:Image):void{
var bitmap:Bitmap = ((target as Image).content as Bitmap);
if (bitmap != null) {
bitmap.smoothing = true;
}
}

private function addImage():void {
var newImg:Image = new Image();
newImg.source = new Bitmap(Bitmap(iPreview.content).bitmapData);
newImg.toolTip = iPath.text;
newImg.maintainAspectRatio = false;
newImg.scaleContent = true;
newImg.width = iWidth.value;
newImg.height = iHeight.value;
images.addElement(newImg);
smoothImage(newImg);
newImg.validateNow();
newImg.x = iX.value;
newImg.y = iY.value;
newImg.doubleClickEnabled = true;
newImg.addEventListener(MouseEvent.ROLL_OVER, imgOver);
newImg.addEventListener(MouseEvent.ROLL_OUT, imgOut);
newImg.addEventListener(MouseEvent.DOUBLE_CLICK, imgClick);
newImg.addEventListener(MouseEvent.MOUSE_DOWN, imgDown);
newImg.addEventListener(MouseEvent.MOUSE_MOVE, imgMove);
newImg.buttonMode = true;
}

private function editImage():void {
var editImg:* = currentlyEditing;
editImg.source = new Bitmap(Bitmap(iPreview.content).bitmapData);
editImg.toolTip = iPath.text;
editImg.width = iWidth.value;
editImg.height = iHeight.value;
editImg.x = iX.value;
editImg.y = iY.value;
updateImgLimits();
}

private function deleteImage():void {
images.removeElement(currentlyEditing);
}

private function imgOver(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 1) {
evt.target.alpha = 0.7;
currentImage = evt.currentTarget;
createAnchors();
enableResize();
positionResize();
}
}

private function imgOut(evt:MouseEvent):void{
evt.target.alpha = 1;
if (toolbarStack.selectedIndex == 1 && currentlyDragging == null) {
cursorManager.removeAllCursors();
}
}

private function imgClick(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 1) {
currentlyEditing = evt.currentTarget;
PopUpManager.addPopUp(imageWindow, this, true);
PopUpManager.centerPopUp(imageWindow);
imageWindow.title = "Edit image";
iPath.text = evt.currentTarget.toolTip;
iPreview.source = new Bitmap(Bitmap(evt.currentTarget.content).bitmapData);
iWidth.value = evt.currentTarget.width;
iHeight.value = evt.currentTarget.height;
iX.value = evt.currentTarget.x;
iY.value = evt.currentTarget.y;
iX.maximum = content.width;
iY.maximum = content.height;
biNew.enabled = false;
biUpdate.enabled = true;
biDelete.enabled = true;
}
}

private function imgDown(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 1) {
currentlyDragging = evt.currentTarget;
dragOffsetX = evt.currentTarget.mouseX;
dragOffsetY = evt.currentTarget.mouseY;
}
}

private function imgMove(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 1 && currentlyDragging && !drawHitArea.mouseEnabled) {
images.setElementIndex(currentlyDragging, images.numElements - 1);
cursorManager.setCursor(moveCursor, 2, -10, -10);
drawHitArea.mouseEnabled = true;
drawHitArea.x = -1;
drawHitArea.y = 0;
drawHitArea.width = content.width;
drawHitArea.height = content.height;
}
}

private function updateImgLimits():void {
iX.maximum = content.width - iWidth.value;
iY.maximum = content.height - iHeight.value;
iWidth.maximum = content.width;
iHeight.maximum = content.height;
}

private function createAnnAnchors():void {
if (annAnchors == null) {
annAnchors = [];
for (var i:int = 0; i < 2; i++) {
var newAnchor:SpriteVisualElement = new SpriteVisualElement();
newAnchor.width = 10;
newAnchor.height = 10;
newAnchor.graphics.lineStyle(2, 0x000000);
newAnchor.graphics.beginFill(0xffffff);
newAnchor.graphics.drawRect(0, 0, 10, 10);
anchorsAnn.addElement(newAnchor);
annAnchors.push(newAnchor);
newAnchor.addEventListener(MouseEvent.ROLL_OVER, anchorAnnOver);
newAnchor.addEventListener(MouseEvent.ROLL_OUT, anchorAnnOut);
newAnchor.addEventListener(MouseEvent.MOUSE_DOWN, anchorAnnDown);
}
}
/* Layout:
- - -
0 - 1
- - -*/
}

private function anchorAnnOver(evt:MouseEvent):void {
if(anchorsAnn.alpha>0){
cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10);
}
}

private function anchorAnnOut(evt:MouseEvent):void {
cursorManager.removeAllCursors();
}

private function anchorAnnDown(evt:MouseEvent):void {
ann_positions = [currentAnn.x, currentAnn.width];
switch(evt.currentTarget) {
case annAnchors[0]: ann_currentDir = "left"; ann_anchorX = currentAnn.x + currentAnn.width; break;
case annAnchors[1]: ann_currentDir = "right"; ann_anchorX = currentAnn.x; break;
}
ann_resizing = true;
drawHitArea.mouseEnabled = true;
drawHitArea.width = content.width;
drawHitArea.height = content.height;
drawHitArea.x = -1;
drawHitArea.y = 1;
}

private function annResize():void {
if (ann_resizing) {
cursorManager.removeAllCursors();
var mouseX:Number = drawArea.mouseX;
if (ann_currentDir == "left" && mouseX >= 0 && mouseX < ann_anchorX) {
ann_positions = [mouseX, ann_anchorX - mouseX];
cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10);
}
if (ann_currentDir == "right" && mouseX < content.width && mouseX > ann_anchorX) {
ann_positions = [ann_anchorX, mouseX - ann_anchorX];
cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10);
}

if(ann_positions[0] >=0 && ann_positions[1] + ann_positions[0]<=content.width){
updateAnn();
positionAnnResize();
}
}
}

private function updateAnn():void {
currentAnn.x = ann_positions[0];
currentAnn.width = ann_positions[1];
currentAnn.heightInLines = NaN;
annWidth.value = currentAnn.width;
updateAnnCoordLimits();
}

private function enableAnnResize():void {
anchorsAnn.alpha = 0.8;
anchorsAnn.mouseEnabled = true;
anchorsAnn.mouseChildren = true;
}

private function disableAnnResize():void {
if(toolbarStack.selectedIndex==3 && !ann_resizing){
anchorsAnn.alpha = 0;
anchorsAnn.mouseEnabled = false;
anchorsAnn.mouseChildren = false;
}
}

private function positionAnnResize():void {
currentAnn.heightInLines = NaN;
var cX:Number = currentAnn.x;
var cY:Number = currentAnn.y;
var cW:Number = currentAnn.width;
var cH:Number = currentAnn.height;
annAnchors[0].x = cX - 5;
annAnchors[0].y = cY + cH/2 - 5;
annAnchors[1].x = cX + cW - 5;
annAnchors[1].y = cY + cH/2 - 5;
}

/////////////////////////////////////////////////

private function createAnchors():void {
if (imageAnchors == null) {
imageAnchors = [];
for (var i:int = 0; i < 8; i++) {
var newAnchor:SpriteVisualElement = new SpriteVisualElement();
newAnchor.width = 10;
newAnchor.height = 10;
newAnchor.graphics.lineStyle(2, 0x000000);
newAnchor.graphics.beginFill(0xffffff);
newAnchor.graphics.drawRect(0, 0, 10, 10);
anchors.addElement(newAnchor);
imageAnchors.push(newAnchor);
newAnchor.addEventListener(MouseEvent.ROLL_OVER, anchorOver);
newAnchor.addEventListener(MouseEvent.ROLL_OUT, anchorOut);
newAnchor.addEventListener(MouseEvent.MOUSE_DOWN, anchorDown);
}
}
/* Layout:
0 1 2
3 - 4
5 6 7*/
}

private function anchorOver(evt:MouseEvent):void {
if(anchors.alpha>0){
switch(evt.currentTarget) {
case imageAnchors[0]: case imageAnchors[7]: cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10); break;
case imageAnchors[2]: case imageAnchors[5]: cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10); break;
case imageAnchors[1]: case imageAnchors[6]: cursorManager.setCursor(resizeVerticalCursor, 2, -9, -10); break;
case imageAnchors[3]: case imageAnchors[4]: cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10); break;
}
}
}

private function anchorOut(evt:MouseEvent):void {
cursorManager.removeAllCursors();
}

private function anchorDown(evt:MouseEvent):void {
currentRatio = currentImage.width / currentImage.height;
img_rectangle = [currentImage.x, currentImage.y, currentImage.width, currentImage.height];
switch(evt.currentTarget) {
case imageAnchors[0]: img_currentDir = "upLeft"; img_anchorY = img_rectangle[1] + img_rectangle[3]; img_anchorX = img_rectangle[0] + img_rectangle[2]; break;
case imageAnchors[1]: img_currentDir = "up"; img_anchorY = img_rectangle[1] + img_rectangle[3]; break;
case imageAnchors[2]: img_currentDir = "upRight"; img_anchorY = img_rectangle[1] + img_rectangle[3]; img_anchorX = img_rectangle[0]; break;
case imageAnchors[3]: img_currentDir = "left"; img_anchorX = img_rectangle[0] + img_rectangle[2]; break;
case imageAnchors[4]: img_currentDir = "right"; img_anchorX = img_rectangle[0]; break;
case imageAnchors[5]: img_currentDir = "downLeft"; img_anchorY = img_rectangle[1]; img_anchorX = img_rectangle[0] + img_rectangle[2]; break;
case imageAnchors[6]: img_currentDir = "down"; img_anchorY = img_rectangle[1]; break;
case imageAnchors[7]: img_currentDir = "downRight"; img_anchorY = img_rectangle[1]; img_anchorX = img_rectangle[0]; break;
}
img_resizing = true;
drawHitArea.mouseEnabled = true;
drawHitArea.width = content.width;
drawHitArea.height = content.height;
drawHitArea.x = -1;
drawHitArea.y = 1;
}

private function imageResize():void {
if (img_resizing) {
cursorManager.removeAllCursors();
var prevRect:Array = img_rectangle;
var mouseX:Number = drawArea.mouseX;
var mouseY:Number = drawArea.mouseY;
if (img_currentDir == "up" && mouseY >= 0 && mouseY < img_anchorY) {
img_rectangle = [prevRect[0], mouseY - 1, prevRect[2], img_anchorY - mouseY + 1]; cursorManager.setCursor(resizeVerticalCursor, 2, -9, -10);
}
if (img_currentDir == "down" && mouseY < content.height && mouseY > img_anchorY) {
img_rectangle = [prevRect[0], img_anchorY, prevRect[2], mouseY - img_anchorY]; cursorManager.setCursor(resizeVerticalCursor, 2, -9, -10);
}
if (img_currentDir == "left" && mouseX >= 0 && mouseX < img_anchorX) {
img_rectangle = [mouseX, prevRect[1], img_anchorX - mouseX, prevRect[3]]; cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10);
}
if (img_currentDir == "right" && mouseX < content.width && mouseX > img_anchorX) {
img_rectangle = [img_anchorX, prevRect[1], mouseX - img_anchorX, prevRect[3]]; cursorManager.setCursor(resizeHorizontalCursor, 2, -9, -10);
}
if (img_currentDir == "upLeft" && mouseY >= 0 && mouseY < img_anchorY && mouseX >= 0 && mouseX < img_anchorX) {
if (!isShift) img_rectangle = [mouseX, mouseY - 1, img_anchorX - mouseX, img_anchorY - mouseY + 1]; cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10);
if (isShift) img_rectangle = [mouseX, img_anchorY - (img_anchorX - mouseX)/currentRatio, img_anchorX - mouseX, (img_anchorX - mouseX)/currentRatio]; cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10);
}
if (img_currentDir == "upRight" && mouseY >= 0 && mouseY < img_anchorY && mouseX < content.width && mouseX > img_anchorX) {
if (!isShift) img_rectangle = [img_anchorX, mouseY - 1, mouseX - img_anchorX, img_anchorY - mouseY + 1]; cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10);
if (isShift) img_rectangle = [img_anchorX, img_anchorY - (mouseX - img_anchorX)/currentRatio, mouseX - img_anchorX, (mouseX - img_anchorX)/currentRatio]; cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10);
}
if (img_currentDir == "downLeft" && mouseY < content.height && mouseY > img_anchorY && mouseX >= 0 && mouseX < img_anchorX) {
if (!isShift) img_rectangle = [mouseX, img_anchorY, img_anchorX - mouseX, mouseY - img_anchorY]; cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10);
if (isShift) img_rectangle = [mouseX, img_anchorY, img_anchorX - mouseX, (img_anchorX - mouseX)/currentRatio]; cursorManager.setCursor(resizeDiag1Cursor, 2, -9, -10);
}
if (img_currentDir == "downRight" && mouseY < content.height && mouseY > img_anchorY && mouseX < content.width && mouseX > img_anchorX) {
if (!isShift) img_rectangle = [img_anchorX, img_anchorY, mouseX - img_anchorX, mouseY - img_anchorY]; cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10);
if (isShift) img_rectangle = [img_anchorX, img_anchorY, mouseX - img_anchorX, (mouseX - img_anchorX)/currentRatio]; cursorManager.setCursor(resizeDiag2Cursor, 2, -9, -10);
}

if(img_rectangle[0]>=0 && img_rectangle[1]>=0 && img_rectangle[2] + img_rectangle[0]<=content.width && img_rectangle[3] + img_rectangle[1]<=content.height){
updateImage();
positionResize();
}
}
}

private function updateImage():void {
currentImage.x = img_rectangle[0];
currentImage.y = img_rectangle[1];
currentImage.width = img_rectangle[2];
currentImage.height = img_rectangle[3];
}

private function enableResize():void {
anchors.alpha = 0.8;
anchors.mouseEnabled = true;
anchors.mouseChildren = true;
}

private function disableResize():void {
if(toolbarStack.selectedIndex==1 && !img_resizing){
anchors.alpha = 0;
anchors.mouseEnabled = false;
anchors.mouseChildren = false;
}
}

private function positionResize():void {
var cX:Number = currentImage.x;
var cY:Number = currentImage.y;
var cW:Number = currentImage.width;
var cH:Number = currentImage.height;
imageAnchors[0].x = cX - 5;
imageAnchors[0].y = cY - 5;
imageAnchors[1].x = cX + cW/2 - 5;
imageAnchors[1].y = cY - 5;
imageAnchors[2].x = cX + cW - 5;
imageAnchors[2].y = cY - 5;
imageAnchors[3].x = cX - 5;
imageAnchors[3].y = cY + cH/2 - 5;
imageAnchors[4].x = cX + cW - 5;
imageAnchors[4].y = cY + cH/2 - 5;
imageAnchors[5].x = cX - 5;
imageAnchors[5].y = cY + cH - 5;
imageAnchors[6].x = cX + cW/2 - 5;
imageAnchors[6].y = cY + cH - 5;
imageAnchors[7].x = cX + cW - 5;
imageAnchors[7].y = cY + cH - 5;
}

private function openAbout():void{
PopUpManager.addPopUp(aboutWindow, this, true);
PopUpManager.centerPopUp(aboutWindow);
}

private function closeAbout():void{
PopUpManager.removePopUp(aboutWindow);
}
]]>
</fx:Script>

<fx:Declarations>
<mx:ArrayCollection id="toolbarToggle">
<fx:Object icon="@Embed(../lib/ic_pen.png)" />
<fx:Object icon="@Embed(../lib/ic_ellipse.png)"/>
<fx:Object icon="@Embed(../lib/ic_rect.png)"/>
<fx:Object icon="@Embed(../lib/ic_line.png)"/>
<fx:Object icon="@Embed(../lib/ic_arrow.png)"/>
<fx:Object icon="@Embed(../lib/ic_eyedropper.png)"/>
<fx:Object icon="@Embed(../lib/ic_eraser.png)"/>
</mx:ArrayCollection>
<s:TitleWindow id="imageWindow" width="450" height="370" close="closeImage();" >
<s:VGroup width="500" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:HGroup width="100%">
<s:TextInput editable="false" width="350" id="iPath" />
<s:Button label="Browse.." click="imageBrowse();" />
</s:HGroup>
<mx:Box backgroundColor="#dddddd" width="430" height="180" verticalAlign="middle" horizontalAlign="center" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Image id="iPreview" scaleContent="true" maxWidth="430" maxHeight="180" maintainAspectRatio="true" smoothBitmapContent="true"/>
</mx:Box>
<s:HGroup verticalAlign="middle">
<s:Label>Width:</s:Label>
<s:NumericStepper minimum="10" maximum="2000" id="iWidth" change="updateImgLimits();" />
<s:Label>Height:</s:Label>
<s:NumericStepper minimum="10" maximum="2000" id="iHeight" change="updateImgLimits();" />
<s:Label id="imgOriginal" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Coordinates: X</s:Label>
<s:NumericStepper minimum="0" id="iX" change="updateImgLimits();" />
<s:Label>Y</s:Label>
<s:NumericStepper minimum="0" id="iY" change="updateImgLimits();" />
</s:HGroup>
<s:HGroup>
<s:Button label="Add image" click="addImage(); closeImage();" id="biNew" />
<s:Button label="Update image" click="editImage(); closeImage();" id="biUpdate" />
<s:Button label="Delete image" click="deleteImage(); closeImage();" id="biDelete" />
</s:HGroup>
<mx:Image id="iPreviewHidden" scaleContent="false" visible="false" maintainAspectRatio="true" smoothBitmapContent="true"/>
</s:VGroup>
</s:TitleWindow>
<s:TitleWindow id="saveWindow" title="Save picture" width="200" height="120" close="closeSave();" >
<s:VGroup width="200" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:HGroup verticalAlign="middle">
<s:Label>Export format:</s:Label>
<s:RadioButton id="radioJPG" label="JPG" groupName="saveFormat" change="changeSelectedFormat(jpg);" />
<s:RadioButton id="radioPNG" label="PNG" groupName="saveFormat" change="changeSelectedFormat(png);" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label enabled="{radioJPG.selected}">JPG quality:</s:Label>
<s:HSlider id="sliderJPG" minimum="1" maximum="100" enabled="{radioJPG.selected}" change="changeSelectedQuality();" />
</s:HGroup>
<s:Button label="Export file" width="100%" click="closeSave(); startExport();" />
</s:VGroup>
</s:TitleWindow>
<s:TitleWindow id="newfileWindow" title="Start new..." width="200" height="100" close="closeNewfile();" >
<mx:ViewStack id="newfileStack">
<s:NavigatorContent>
<s:VGroup width="200" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:Button label="Import new picture" width="100%" click="closeNewfile(); importPicture();" />
<s:Button label="Create blank page" width="100%" click="newfileStack.selectedIndex = 1; newfileWindow.height = 160;" />
<s:Button label="Import screenshot" width="100%" click="closeNewfile(); importScreenshot();" enabled="{screenshotAvailable}" />
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="200" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:HGroup verticalAlign="middle">
<s:Label>Width:</s:Label>
<s:NumericStepper minimum="10" maximum="2000" value="400" id="newWidth" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Height:</s:Label>
<s:NumericStepper minimum="10" maximum="2000" value="300" id="newHeight" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Background color:</s:Label>
<mx:ColorPicker id="newColor" selectedColor="0xffffff" />
</s:HGroup>
<s:Button label="Create blank" width="100%" click="closeNewfile(); blankPicture();" />
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</s:TitleWindow>
<s:TitleWindow id="annotationWindow" width="500" height="400" close="closeAnnotation();" >
<s:VGroup width="500" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:Label>Text:</s:Label>
<s:TextArea width="100%" height="100" id="annText"/>
<s:HGroup verticalAlign="middle">
<s:Label>Rectangle color:</s:Label>
<mx:ColorPicker selectedColor="@{aBodyColor}" toolTip="Annotation body color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Rectangle visibility:</s:Label>
<mx:HSlider value="@{aBodyAlpha}" toolTip="Annotation body visibility" minimum="0" maximum="1" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Text color:</s:Label>
<mx:ColorPicker selectedColor="@{aTextColor}" toolTip="Annotation text color" />
</s:HGroup>
<s:HGroup verticalAlign="middle" >
<s:Label>Text size:</s:Label>
<s:NumericStepper value="@{aTextSize}" toolTip="Text size" minimum="8" maximum="100" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Annotation width:</s:Label>
<s:NumericStepper minimum="10" id="annWidth" change="updateAnnCoordLimits();" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Coordinates: X</s:Label>
<s:NumericStepper minimum="0" id="annX"/>
<s:Label>Y</s:Label>
<s:NumericStepper minimum="0" id="annY" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:CheckBox label="Enable border?" id="annBorder" />
</s:HGroup>
<s:HGroup>
<s:Button label="Create annotation" click="addAnnotation(); closeAnnotation();" id="baNew" />
<s:Button label="Edit annotation" click="editAnnotation(); closeAnnotation();" id="baEdit" />
<s:Button label="Delete annotation" click="deleteAnnotation(); closeAnnotation();" id="baDelete" />
</s:HGroup>
</s:VGroup>
</s:TitleWindow>
<s:TitleWindow id="aboutWindow" width="500" height="160" close="closeAbout();" title="About KirAnnotator">
<s:HGroup>
<mx:Image source="@Embed(../bin/icons/KirAnnotator128.png)"/>
<s:Group width="372">
<s:VGroup top="10" left="10" right="10" bottom="10" width="100%">
<s:Label width="100%">KirAnnotator is a free open-source AIR application developed by Kirill Poletaev.</s:Label>
<s:Label width="100%">You can find a step-by-step tutorial series of making this program at kirill-poletaev.blogspot.com.</s:Label>
<s:Label width="100%">Contact me at lirx3m@hotmail.com.</s:Label>
<s:Label width="100%">I hope you enjoy this app and find it useful!</s:Label>
</s:VGroup>
</s:Group>
</s:HGroup>
</s:TitleWindow>
</fx:Declarations>

<s:VGroup width="100%" height="100%" gap="0">
<mx:HBox backgroundColor="#ccccdd" width="100%" height="60">
<s:VGroup gap="0" width="100%" height="100%">
<s:HGroup gap="0" width="100%">
<custom:IconButton icon="@Embed(../lib/new.png)" toolTip="New" enabled="{buttonsEnabled}" buttonMode="true" click="newFile();" focusEnabled="false" />
<custom:IconButton icon="@Embed(../lib/images.png)" toolTip="Images" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=1;" focusEnabled="false"/>
<custom:IconButton icon="@Embed(../lib/draw.png)" toolTip="Draw" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=2;" focusEnabled="false"/>
<custom:IconButton icon="@Embed(../lib/bubble.png)" toolTip="Annotation" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=3;" focusEnabled="false"/>
<custom:IconButton icon="@Embed(../lib/cut.png)" toolTip="Cut" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=4;" focusEnabled="false"/>
<custom:IconButton id="saveBtn" icon="@Embed(../lib/save.png)" toolTip="Save" enabled="{buttonsEnabled}" buttonMode="true" click="saveFile();" focusEnabled="false"/>
<mx:ViewStack id="toolbarStack" width="466" height="100%" change="toolbarChange();">
<s:NavigatorContent>

</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Images</s:Label>
<s:HGroup verticalAlign="middle">
<s:Label>Move and resize images with mouse, double-click to edit.</s:Label>
<custom:SmallButton label="Add a new image" icon="@Embed(../lib/bubble_add.png)" click="openImage();"/>
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Draw</s:Label>
<s:HGroup>
<custom:SmallButton icon="@Embed(../lib/arrow_undo.png)" enabled="{undo_canUndo}" click="doUndo();" />
<custom:SmallButton icon="@Embed(../lib/arrow_redo.png)" enabled="{undo_canRedo}" click="doRedo();" />
<mx:ToggleButtonBar dataProvider="{toolbarToggle}" width="150" id="drawTools" />
<mx:ColorPicker selectedColor="@{lineColor}" change="updateLineExample();" />
<mx:Slider minimum="1" maximum="10" change="updateLineExample();" value="@{lineThickness}" width="80" liveDragging="true"/>
<s:SpriteVisualElement width="40" height="20" id="lineExample"/>
<s:Button click="clearDrawBitmap();" label="Clear all" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Annotation</s:Label>
<s:HGroup verticalAlign="middle">
<s:Label>Move annotations by dragging them, double-click to edit.</s:Label>
<custom:SmallButton label="Add a new annotation" icon="@Embed(../lib/bubble_add.png)" click="openAnnotation();"/>
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Cut</s:Label>
<s:HGroup verticalAlign="middle">
<s:Label>Select an area to crop.</s:Label>
<custom:SmallButton label="Deselect" icon="@Embed(../lib/cross.png)" click="crop_deselect();"/>
<custom:SmallButton label="Change selection display" icon="@Embed(../lib/shape_handles.png)" click="crop_changeSelectionType();"/>
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
<mx:Box width="100%" horizontalAlign="right">
<custom:SmallButton width="16" icon="@Embed(../lib/help.png)" click="openAbout();" />
</mx:Box>
</s:HGroup>
<mx:Box backgroundColor="#bbbbcc" width="100%" height="100%">
<s:Group width="100%" height="100%">
<mx:Box backgroundColor="#ffffff" width="52" height="100%" left="{52 * toolbarStack.selectedIndex}" visible="{buttonsEnabled}" />
</s:Group>
</mx:Box>
</s:VGroup>
</mx:HBox>
<mx:Canvas width="100%" height="100%" horizontalScrollPolicy="on" verticalScrollPolicy="on" rollOver="canvasOver();" rollOut="canvasOut();" id="canvas" mouseMove="moveErase();" mouseDown="startErase();" scroll="updateErase();">
<s:Group width="100%" height="100%">
<mx:Box backgroundColor="#eeeeee" width="100%" height="100%" verticalAlign="middle" horizontalAlign="center" horizontalScrollPolicy="off" verticalScrollPolicy="off" >
<mx:Box id="drawArea" backgroundColor="#ffffff" width="600" height="200" horizontalScrollPolicy="off" verticalScrollPolicy="off" rollOut="crop_areaRollOut();">
<mx:ViewStack id="stack" change="stackChange();">
<s:NavigatorContent>
<s:HGroup gap="0">
<custom:BigButton icon="@Embed(../lib/folder.png)" subtext="Import picture" toolTip="Open file" enabled="true" buttonMode="true" bWidth="200" bHeight="200" click="importPicture();" />
<custom:BigButton icon="@Embed(../lib/blank.png)" subtext="Create blank" toolTip="Blank page" enabled="true" buttonMode="true" bWidth="200" bHeight="200" click="newFile(); newfileStack.selectedIndex = 1; newfileWindow.height = 160;" />
<custom:BigButton icon="@Embed(../lib/camera.png)" subtext="Screenshot" toolTip="Use current screenshot in the clipboard" buttonMode="true" bWidth="200" bHeight="200" click="importScreenshot(); checkScreenshot = false;" enabled="{screenshotAvailable}" />
</s:HGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<mx:Image id="imgHolder" />
<s:Group id="drawGroup">
<mx:Image id="drawBitmap" />
<s:Group id="tempSprite" />
</s:Group>
<mx:Box id="annHitArea" alpha="0" backgroundColor="#000000" click="openAnnotation(drawArea.mouseX, drawArea.mouseY);" rollOver="disableResize(); disableAnnResize();"/>
<s:Group id="images" />
<s:Group id="annotations" />
<s:Group id="cropDraw" mouseEnabled="false" />
<s:Group id="anchors"/>
<s:Group id="anchorsAnn"/>
<mx:Box id="drawHitArea" alpha="0" backgroundColor="#000000" mouseDown="drawDown();" mouseMove="drawMove();"/>
</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</mx:Box>
<s:Group id="eraser" mouseEnabled="false" mouseChildren="false" alpha="0" />
</s:Group>
</mx:Canvas>
</s:VGroup>

</s:WindowedApplication>

Thanks for reading!
Read more »

Monday, January 19, 2015

Creating a Flex AIR Annotator app Part 15

In this part we will make it possible to edit the annotations that were created.

First of all, we will change the contents of our annotation navigator content and leave only one button - the Create new annotation one. We will change that too - add a label property to the SmallButton object like this:

<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Annotation</s:Label>
<s:HGroup verticalAlign="middle">
<s:Label>Click an annotation to edit it, or </s:Label>
<custom:SmallButton label="Create a new annotation" icon="@Embed(../lib/bubble_add.png)" click="openAnnotation();"/>
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>

Go to smallSkin.mxml file and change it to look like this:

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin name="iconSkin"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="20" minHeight="20"
alpha.disabled="0.5">

<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>

<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>


<s:Rect width="100%" height="100%" alpha.up="0" alpha.over="0.4" alpha.disabled="0">
<s:fill>
<s:SolidColor color="#ffffff"/>
</s:fill>
</s:Rect>
<s:HGroup width="100%" height="100%" verticalAlign="middle">
<s:BitmapImage source="{hostComponent.getStyle(icon)}" top="2" right="2" left="2" bottom="2" alpha.disabled="0.5" />
<s:Label id="labelDisplay" color="#000066" />
</s:HGroup>

</s:SparkSkin>

Now not only it can display icons, it has text too!

Go back to main mxml file, find the declarations tags and change the TitleWindow inside of it to look like this:

<s:TitleWindow id="annotationWindow" width="500" height="300" close="closeAnnotation();" >
<s:VGroup width="500" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:Label>Text:</s:Label>
<s:TextArea width="100%" height="100" id="annText" />
<s:HGroup verticalAlign="middle">
<s:Label>Rectangle color:</s:Label>
<mx:ColorPicker selectedColor="@{aBodyColor}" toolTip="Annotation body color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Text color:</s:Label>
<mx:ColorPicker selectedColor="@{aTextColor}" toolTip="Annotation text color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Annotation width:</s:Label>
<s:NumericStepper minimum="10" id="annWidth" />
</s:HGroup>
<s:HGroup>
<s:Button label="Create annotation" click="addAnnotation(); closeAnnotation();" id="baNew" />
<s:Button label="Edit annotation" click="editAnnotation(); closeAnnotation();" id="baEdit" />
<s:Button label="Delete annotation" click="closeAnnotation();" id="baDelete" />
</s:HGroup>
</s:VGroup>
</s:TitleWindow>

As you can see, I added 2 more buttons - Edit annotation and Delete annotation. Set the Edit buttons click event handlers to editAnnotation() and closeAnnotation(), then the Delete buttons click event handler to just closeAnnotation():

<s:TitleWindow id="annotationWindow" width="500" height="300" close="closeAnnotation();" >
<s:VGroup width="500" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:Label>Text:</s:Label>
<s:TextArea width="100%" height="100" id="annText" />
<s:HGroup verticalAlign="middle">
<s:Label>Rectangle color:</s:Label>
<mx:ColorPicker selectedColor="@{aBodyColor}" toolTip="Annotation body color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Text color:</s:Label>
<mx:ColorPicker selectedColor="@{aTextColor}" toolTip="Annotation text color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Annotation width:</s:Label>
<s:NumericStepper minimum="10" id="annWidth" />
</s:HGroup>
<s:HGroup>
<s:Button label="Create annotation" click="addAnnotation(); closeAnnotation();" id="baNew" />
<s:Button label="Edit annotation" click="editAnnotation(); closeAnnotation();" id="baEdit" />
<s:Button label="Delete annotation" click="closeAnnotation();" id="baDelete" />
</s:HGroup>
</s:VGroup>
</s:TitleWindow>

Go to addAnnotation() and add 3 mouse event listeners inside of it, also set buttonMode to true:

private function addAnnotation():void {
var newAnn:spark.components.TextArea = new spark.components.TextArea();
newAnn.text = annText.text;
newAnn.width = annWidth.value;
newAnn.setStyle("contentBackgroundColor", aBodyColor);
newAnn.setStyle("color", aTextColor);
newAnn.editable = false;
newAnn.selectable = false;
annotations.addElement(newAnn);
newAnn.validateNow();
newAnn.heightInLines = NaN;
newAnn.x = drawArea.width / 2 - newAnn.width / 2;
newAnn.y = 0;
newAnn.addEventListener(MouseEvent.ROLL_OVER, annOver);
newAnn.addEventListener(MouseEvent.ROLL_OUT, annOut);
newAnn.addEventListener(MouseEvent.CLICK, annClick);
newAnn.buttonMode = true;
}

The first two event handlers, annOver and annOut, set alpha of the button to 0.5 and 1:

private function annOver(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
evt.target.alpha = 0.5;
}
}

private function annOut(evt:MouseEvent):void{
evt.target.alpha = 1;
}

The annClick() function checks if selectedIndex of toolbarStack is 3 (if the annotations tool is selected), and opens TitleWindow and sets its components values. Also, it sets a variable currentlyEditing to evt.currentTarget, so that we know what text field were editing. Set enabled properties of the 3 buttons to false, true and true:

private function annClick(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
currentlyEditing = evt.currentTarget;
PopUpManager.addPopUp(annotationWindow, this, true);
PopUpManager.centerPopUp(annotationWindow);
annText.text = currentlyEditing.text;
annotationWindow.title = "Edit annotation";
annWidth.maximum = drawArea.width;
annWidth.value = currentlyEditing.width + 2;
aBodyColor = currentlyEditing.getStyle("contentBackgroundColor");
aTextColor = currentlyEditing.getStyle("color");
baNew.enabled = false;
baEdit.enabled = true;
baDelete.enabled = true;
}
}

Lets declare this currentlyEditing variable:

private var currentlyEditing:*;

Now lets create the editAnnotation() function. Here we update the values of currently editing annotation to the values from our TitleWindow:

private function editAnnotation():void {
var editAnn:* = currentlyEditing;
editAnn.text = annText.text;
editAnn.width = annWidth.value;
editAnn.setStyle("contentBackgroundColor", aBodyColor);
editAnn.setStyle("color", aTextColor);
editAnn.heightInLines = NaN;
}

One last thing, update openAnnotation() to add lines that set enabled properties of baNew, baEdit and baDelete:

private function openAnnotation():void {
PopUpManager.addPopUp(annotationWindow, this, true);
PopUpManager.centerPopUp(annotationWindow);
annText.text = "Enter text here";
annotationWindow.title = "Create new annotation";
annWidth.maximum = drawArea.width;
annWidth.value = 200;
baNew.enabled = true;
baEdit.enabled = false;
baDelete.enabled = false;
}

Full code:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:custom="*"
showStatusBar="false"
creationComplete="init();"
applicationComplete="listeners();"
minWidth="740" minHeight="400"
width="740" height="500">

<fx:Script>
<![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.FocusDirection;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.filters.DropShadowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.utils.ByteArray;
import mx.controls.Alert;
import flash.filesystem.FileMode;
import mx.controls.Label;
import mx.controls.TextArea;
import mx.core.FlexGlobals;
import spark.primitives.Rect;
import flash.text.TextFieldAutoSize;
import mx.managers.PopUpManager;

private var bitmapData:BitmapData;
private var content:*;
[Bindable]
private var buttonsEnabled:Boolean = false;
[Bindable]
private var lineThickness:int = 5;
[Bindable]
private var lineColor:uint = 0xff0000;
[Bindable]
private var aBodyColor:uint = 0xffff88;
[Bindable]
private var aTextColor:uint = 0x000000;

[Embed(source="../lib/cursor_cross.png")]
private var crossCursor:Class;
[Embed(source="../lib/ic_eyedropper.png")]
private var pickerCursor:Class;

private var drawMode:String = "";
private var anchorX:Number;
private var anchorY:Number;
private var lastX:Number;
private var lastY:Number;
private var isShift:Boolean = false;
private var temporaryBitmap:BitmapData;
private var currentlyEditing:*;

private function init():void{
drawArea.filters = [new DropShadowFilter(4, 60, 0, 0.7, 10, 10, 1, 3)];
}

private function listeners():void {
addEventListener(MouseEvent.MOUSE_UP, drawUp);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUp);
addEventListener(Event.ENTER_FRAME, everyFrame);
}

private function keyDown(evt:KeyboardEvent):void {
if (evt.keyCode == 16) {
isShift = true;
}
}

private function keyUp(evt:KeyboardEvent):void {
if (evt.keyCode == 16) {
isShift = false;
}
}

private function everyFrame(evt:Event):void {
if (toolbarStack.selectedIndex == 2 && focusManager.getFocus() != saveBtn) {
focusManager.setFocus(saveBtn);
}
}

private function canvasOver():void {
if (toolbarStack.selectedIndex == 2 && drawTools) {
if(drawTools.selectedIndex < 5){
cursorManager.setCursor(crossCursor, 2, -10, -10);
} else {
cursorManager.setCursor(pickerCursor, 2, 0, -18);
}
}
}

private function canvasOut():void {
cursorManager.removeAllCursors();
}

private function drawDown():void {
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 0) {
drawMode = "pen";
drawSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 3) {
drawMode = "line";
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 2) {
drawMode = "rect";
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 1) {
drawMode = "ellipse";
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 4) {
drawMode = "arrow";
tempSprite.graphics.moveTo(drawArea.mouseX, drawArea.mouseY);
anchorX = drawArea.mouseX;
anchorY = drawArea.mouseY;
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (toolbarStack.selectedIndex == 2 && drawTools.selectedIndex == 5) {
drawMode = "picker";
drawTempBitmap();
getPickerColor();
}
}

private function drawUp(evt:MouseEvent):void {
if (drawMode == "line") {
tempSprite.graphics.clear();
drawSprite.graphics.lineStyle(lineThickness, lineColor);
drawSprite.graphics.moveTo(anchorX, anchorY);
drawSprite.graphics.lineTo(lastX, lastY);
}
if (drawMode == "rect") {
tempSprite.graphics.clear();
drawSprite.graphics.lineStyle(lineThickness, lineColor);
var newRectValues:Array;
if (!isShift) {
newRectValues = calculateRectValues(lastX, lastY, anchorX, anchorY);
}
if(isShift){
newRectValues = calculateSquareValues(lastX, lastY, anchorX, anchorY);
}
drawSprite.graphics.drawRect(newRectValues[0], newRectValues[1], newRectValues[2], newRectValues[3]);
}
if (drawMode == "ellipse") {
tempSprite.graphics.clear();
drawSprite.graphics.lineStyle(lineThickness, lineColor);
var newEllipseValues:Array;
if (!isShift) {
newEllipseValues = calculateRectValues(lastX, lastY, anchorX, anchorY);
}
if(isShift){
newEllipseValues = calculateSquareValues(lastX, lastY, anchorX, anchorY);
}
drawSprite.graphics.drawEllipse(newEllipseValues[0], newEllipseValues[1], newEllipseValues[2], newEllipseValues[3]);
}
if (drawMode == "arrow") {
tempSprite.graphics.clear();
var angle:Number = Math.atan2(anchorY - lastY, anchorX - lastX);
var armlength:Number = 10 + 2.5 * lineThickness;
var offset:Number = (24 - 0.5 * lineThickness) * Math.PI / 180;
var arm1x:Number = armlength * Math.cos(angle + offset) + lastX;
var arm1y:Number = armlength * Math.sin(angle + offset) + lastY;
var arm2x:Number = armlength * Math.cos(angle - offset) + lastX;
var arm2y:Number = armlength * Math.sin(angle - offset) + lastY;

if (arm1x > 0 + lineThickness / 2 && arm1x < drawArea.width - lineThickness / 2 &&
arm1y > 0 + lineThickness / 2 && arm1y < drawArea.height - lineThickness / 2 &&
arm2x > 0 + lineThickness / 2 && arm2x < drawArea.width - lineThickness / 2 &&
arm2y > 0 + lineThickness / 2 && arm2y < drawArea.height - lineThickness / 2 &&
lastX > 9 + lineThickness / 2 && lastX < drawArea.width - lineThickness / 2 - 9 &&
lastY > 9 + lineThickness / 2 && lastY < drawArea.height - lineThickness / 2 - 9){
drawSprite.graphics.lineStyle(lineThickness, lineColor, 1, false, "normal", CapsStyle.NONE, JointStyle.MITER);
drawSprite.graphics.moveTo(anchorX, anchorY);
drawSprite.graphics.lineTo(lastX, lastY);

drawSprite.graphics.beginFill(lineColor, 1);
drawSprite.graphics.moveTo(arm1x, arm1y);
drawSprite.graphics.lineTo(lastX, lastY);
drawSprite.graphics.lineTo(arm2x, arm2y);
drawSprite.graphics.lineTo(arm1x, arm1y);
drawSprite.graphics.endFill();
}
}
drawMode = "";
}

private function drawMove():void {
if (drawMode == "pen") {
drawSprite.graphics.lineStyle(lineThickness, lineColor);
drawSprite.graphics.lineTo(drawArea.mouseX, drawArea.mouseY);
}
if (drawMode == "line") {
if (!isShift) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.moveTo(anchorX, anchorY);
tempSprite.graphics.lineTo(drawArea.mouseX, drawArea.mouseY);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (isShift) {
var straightLinePoint:Point = straightLine(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
if (straightLinePoint.x > lineThickness / 2 && straightLinePoint.x < drawArea.width - lineThickness / 2 &&
straightLinePoint.y > lineThickness/2 && straightLinePoint.y < drawArea.height - lineThickness /2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
tempSprite.graphics.moveTo(anchorX, anchorY);
tempSprite.graphics.lineTo(straightLinePoint.x, straightLinePoint.y);
lastX = straightLinePoint.x;
lastY = straightLinePoint.y;
}
}
}
if (drawMode == "rect") {
var newRectValues:Array;
if (!isShift) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newRectValues = calculateRectValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
if(isShift){
newRectValues = calculateSquareValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
tempSprite.graphics.drawRect(newRectValues[0], newRectValues[1], newRectValues[2], newRectValues[3]);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (drawMode == "ellipse") {
var newEllipseValues:Array;
if (!isShift) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newEllipseValues = calculateRectValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
if(isShift){
newEllipseValues = calculateSquareValues(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY);
}
tempSprite.graphics.drawEllipse(newEllipseValues[0], newEllipseValues[1], newEllipseValues[2], newEllipseValues[3]);
lastX = drawArea.mouseX;
lastY = drawArea.mouseY;
}
if (drawMode == "arrow") {
var mouseX:Number = (isShift)?(straightLine(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY).x):(drawArea.mouseX);
var mouseY:Number = (isShift)?(straightLine(drawArea.mouseX, drawArea.mouseY, anchorX, anchorY).y):(drawArea.mouseY);
var angle:Number = Math.atan2(anchorY - mouseY, anchorX - mouseX);
var armlength:Number = 10 + 2.5 * lineThickness;
var offset:Number = (24 - 0.5 * lineThickness) * Math.PI / 180;
var arm1x:Number = armlength * Math.cos(angle + offset) + mouseX;
var arm1y:Number = armlength * Math.sin(angle + offset) + mouseY;
var arm2x:Number = armlength * Math.cos(angle - offset) + mouseX;
var arm2y:Number = armlength * Math.sin(angle - offset) + mouseY;

if (arm1x > 0 + lineThickness / 2 && arm1x < drawArea.width - lineThickness / 2 &&
arm1y > 0 + lineThickness / 2 && arm1y < drawArea.height - lineThickness / 2 &&
arm2x > 0 + lineThickness / 2 && arm2x < drawArea.width - lineThickness / 2 &&
arm2y > 0 + lineThickness / 2 && arm2y < drawArea.height - lineThickness / 2 &&
mouseX > 9 + lineThickness / 2 && mouseX < drawArea.width - lineThickness / 2 - 9 &&
mouseY > 9 + lineThickness / 2 && mouseY < drawArea.height - lineThickness / 2 - 9){
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor, 1, false, "normal", CapsStyle.NONE, JointStyle.MITER);
tempSprite.graphics.moveTo(anchorX, anchorY);
tempSprite.graphics.lineTo(mouseX, mouseY);

tempSprite.graphics.beginFill(lineColor, 1);
tempSprite.graphics.moveTo(arm1x, arm1y);
tempSprite.graphics.lineTo(mouseX, mouseY);
tempSprite.graphics.lineTo(arm2x, arm2y);
tempSprite.graphics.lineTo(arm1x, arm1y);
tempSprite.graphics.endFill();

lastX = mouseX;
lastY = mouseY;
}
}
if (drawMode == "picker") {
getPickerColor();
}
}

private function straightLine(mouseX:Number, mouseY:Number, anchX:Number, anchY:Number):Point {
var p:Point = new Point(0, 0);
var rotation:Number = Math.atan2(mouseY - anchY, mouseX - anchX) * 180 / Math.PI;
if (rotation<-35 && rotation>-55) {
p = new Point(mouseX, anchY - (mouseX - anchX));
}else
if (rotation<-125 && rotation>-145) {
p = new Point(mouseX, (mouseX - anchX) + anchY);
}else
if (rotation>35 && rotation<55) {
p = new Point(mouseX, (mouseX - anchX) + anchY);
}else
if (rotation>125 && rotation<145) {
p = new Point(mouseX, anchY - (mouseX - anchX));
}else
if (Math.abs(mouseX - anchX) > Math.abs(mouseY - anchY)) {
p = new Point(mouseX, anchY);
}else
if (Math.abs(mouseX - anchX) < Math.abs(mouseY - anchY)) {
p = new Point(anchX, mouseY);
}
return p;
}

private function calculateRectValues(currentX:Number, currentY:Number, anchX:Number, anchY:Number):Array {
var newrect:Array = [0, 0, 0, 0];
// bottom - right
if (currentX > anchX && currentY > anchY) {
newrect[0] = anchX;
newrect[1] = anchY-1;
newrect[2] = currentX - anchX;
newrect[3] = currentY - anchY;
}
// bottom - left
if (currentX < anchX && currentY > anchY) {
newrect[0] = currentX;
newrect[1] = anchY-1;
newrect[2] = anchX - currentX;
newrect[3] = currentY - anchY;
}
// top - right
if (currentX > anchX && currentY < anchY) {
newrect[0] = anchX;
newrect[1] = currentY-1;
newrect[2] = currentX - anchX;
newrect[3] = anchY - currentY;
}
// top - left
if (currentX < anchX && currentY < anchY) {
newrect[0] = currentX;
newrect[1] = currentY-1;
newrect[2] = anchX - currentX;
newrect[3] = anchY - currentY;
}
return newrect;
}

private function calculateSquareValues(currentX:Number, currentY:Number, anchX:Number, anchY:Number):Array {
var newrect:Array = [0, 0, 0, 0];
// bottom - right
if (currentX > anchX && currentY > anchY) {
currentY = (currentX - anchX) + anchY;
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = anchX;
newrect[1] = anchY-1;
newrect[2] = currentX - anchX;
newrect[3] = currentY - anchY;
}
}
// bottom - left
if (currentX < anchX && currentY > anchY) {
currentY = anchY - (currentX - anchX);
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = currentX;
newrect[1] = anchY-1;
newrect[2] = anchX - currentX;
newrect[3] = currentY - anchY;
}
}
// top - right
if (currentX > anchX && currentY < anchY) {
currentY = anchY - (currentX - anchX);
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = anchX;
newrect[1] = currentY-1;
newrect[2] = currentX - anchX;
newrect[3] = anchY - currentY;
}
}
// top - left
if (currentX < anchX && currentY < anchY) {
currentY = (currentX - anchX) + anchY;
if (currentY > 0 + lineThickness/2 && currentY < drawArea.height - lineThickness/2) {
tempSprite.graphics.clear();
tempSprite.graphics.lineStyle(lineThickness, lineColor);
newrect[0] = currentX;
newrect[1] = currentY-1;
newrect[2] = anchX - currentX;
newrect[3] = anchY - currentY;
}
}
return newrect;
}

private function importPicture():void {
var file:File = new File();
var imageFilter:FileFilter = new FileFilter("Images", "*.jpg;*jpeg;*.gif;*.png");
file.browseForOpen("Import picture", [imageFilter]);
file.addEventListener(Event.SELECT, fileSelect);
}

private function fileSelect(evt:Event):void {
var file:File = evt.target as File;
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load(new URLRequest(file.url));
}

private function loadComplete(evt:Event):void {
content = evt.target.content;
bitmapData = new BitmapData(content.width, content.height, false);
bitmapData.draw(content, new Matrix(), null, null, null, true);
stackChange(); // needed if selectedIndex was already 1 when the function was called
stack.selectedIndex = 1;
}

private function stackChange():void {
if (stack.selectedIndex == 1) {
imgHolder.source = new Bitmap(bitmapData);
drawArea.width = content.width;
drawArea.height = content.height;
drawGroup.width = content.width;
drawGroup.height = content.height;
buttonsEnabled = true;
drawSprite.graphics.clear();
var anns:Number = annotations.numChildren;
for (var i:int = 0; i < anns; i++){
annotations.removeElementAt(0);
}
drawTempBitmap();
updateDrawHitArea();
}
}

private function toolbarChange():void {
if (toolbarStack.selectedIndex == 2) {
updateLineExample();
drawHitArea.mouseEnabled = true;
}else {
drawHitArea.mouseEnabled = false;
}
}

private function updateLineExample():void{
lineExample.graphics.clear();
lineExample.graphics.lineStyle(lineThickness, lineColor);
lineExample.graphics.moveTo(10, 10);
lineExample.graphics.lineTo(30, 10);

updateDrawHitArea();
}

private function updateDrawHitArea():void {
drawHitArea.width = content.width - lineThickness;
drawHitArea.height = content.height - lineThickness;
drawHitArea.x = lineThickness / 2;
drawHitArea.y = lineThickness / 2;
}

private function drawTempBitmap():void {
temporaryBitmap = new BitmapData(content.width, content.height, false, 0xffffff);
temporaryBitmap.draw(drawArea);
}

private function getPickerColor():void {
lineColor = temporaryBitmap.getPixel(drawArea.mouseX, drawArea.mouseY);
}

private function openAnnotation():void {
PopUpManager.addPopUp(annotationWindow, this, true);
PopUpManager.centerPopUp(annotationWindow);
annText.text = "Enter text here";
annotationWindow.title = "Create new annotation";
annWidth.maximum = drawArea.width;
annWidth.value = 200;
baNew.enabled = true;
baEdit.enabled = false;
baDelete.enabled = false;
}

private function closeAnnotation():void {
PopUpManager.removePopUp(annotationWindow);
}

private function addAnnotation():void {
var newAnn:spark.components.TextArea = new spark.components.TextArea();
newAnn.text = annText.text;
newAnn.width = annWidth.value;
newAnn.setStyle("contentBackgroundColor", aBodyColor);
newAnn.setStyle("color", aTextColor);
newAnn.editable = false;
newAnn.selectable = false;
annotations.addElement(newAnn);
newAnn.validateNow();
newAnn.heightInLines = NaN;
newAnn.x = drawArea.width / 2 - newAnn.width / 2;
newAnn.y = 0;
newAnn.addEventListener(MouseEvent.ROLL_OVER, annOver);
newAnn.addEventListener(MouseEvent.ROLL_OUT, annOut);
newAnn.addEventListener(MouseEvent.CLICK, annClick);
newAnn.buttonMode = true;
}

private function editAnnotation():void {
var editAnn:* = currentlyEditing;
editAnn.text = annText.text;
editAnn.width = annWidth.value;
editAnn.setStyle("contentBackgroundColor", aBodyColor);
editAnn.setStyle("color", aTextColor);
editAnn.heightInLines = NaN;
}

private function annOver(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
evt.target.alpha = 0.5;
}
}

private function annOut(evt:MouseEvent):void{
evt.target.alpha = 1;
}

private function annClick(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
currentlyEditing = evt.currentTarget;
PopUpManager.addPopUp(annotationWindow, this, true);
PopUpManager.centerPopUp(annotationWindow);
annText.text = currentlyEditing.text;
annotationWindow.title = "Edit annotation";
annWidth.maximum = drawArea.width;
annWidth.value = currentlyEditing.width + 2;
aBodyColor = currentlyEditing.getStyle("contentBackgroundColor");
aTextColor = currentlyEditing.getStyle("color");
baNew.enabled = false;
baEdit.enabled = true;
baDelete.enabled = true;
}
}
]]>
</fx:Script>

<fx:Declarations>
<mx:ArrayCollection id="toolbarToggle">
<fx:Object icon="@Embed(../lib/ic_pen.png)" />
<fx:Object icon="@Embed(../lib/ic_ellipse.png)"/>
<fx:Object icon="@Embed(../lib/ic_rect.png)"/>
<fx:Object icon="@Embed(../lib/ic_line.png)"/>
<fx:Object icon="@Embed(../lib/ic_arrow.png)"/>
<fx:Object icon="@Embed(../lib/ic_eyedropper.png)"/>
</mx:ArrayCollection>
<s:TitleWindow id="annotationWindow" width="500" height="300" close="closeAnnotation();" >
<s:VGroup width="500" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">
<s:Label>Text:</s:Label>
<s:TextArea width="100%" height="100" id="annText" />
<s:HGroup verticalAlign="middle">
<s:Label>Rectangle color:</s:Label>
<mx:ColorPicker selectedColor="@{aBodyColor}" toolTip="Annotation body color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Text color:</s:Label>
<mx:ColorPicker selectedColor="@{aTextColor}" toolTip="Annotation text color" />
</s:HGroup>
<s:HGroup verticalAlign="middle">
<s:Label>Annotation width:</s:Label>
<s:NumericStepper minimum="10" id="annWidth" />
</s:HGroup>
<s:HGroup>
<s:Button label="Create annotation" click="addAnnotation(); closeAnnotation();" id="baNew" />
<s:Button label="Edit annotation" click="editAnnotation(); closeAnnotation();" id="baEdit" />
<s:Button label="Delete annotation" click="closeAnnotation();" id="baDelete" />
</s:HGroup>
</s:VGroup>
</s:TitleWindow>
</fx:Declarations>

<s:VGroup width="100%" height="100%" gap="0">
<mx:HBox backgroundColor="#ccccdd" width="100%" height="52">
<custom:IconButton icon="@Embed(../lib/new.png)" toolTip="New" enabled="{buttonsEnabled}" buttonMode="true" click="importPicture();" />
<custom:IconButton icon="@Embed(../lib/move.png)" toolTip="Move" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=1;" />
<custom:IconButton icon="@Embed(../lib/draw.png)" toolTip="Draw" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=2;" />
<custom:IconButton icon="@Embed(../lib/bubble.png)" toolTip="Annotation" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=3;" />
<custom:IconButton icon="@Embed(../lib/cut.png)" toolTip="Cut" enabled="{buttonsEnabled}" buttonMode="true" click="toolbarStack.selectedIndex=4;" />
<custom:IconButton id="saveBtn" icon="@Embed(../lib/save.png)" toolTip="Save" enabled="{buttonsEnabled}" buttonMode="true" />
<mx:ViewStack id="toolbarStack" width="100%" height="100%" change="toolbarChange();">
<s:NavigatorContent>

</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Move</s:Label>
<s:Label>Click and hold your mouse on annotations to move them.</s:Label>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Draw</s:Label>
<s:HGroup>
<mx:ToggleButtonBar dataProvider="{toolbarToggle}" width="130" id="drawTools" />
<mx:ColorPicker selectedColor="@{lineColor}" change="updateLineExample();" />
<mx:Slider minimum="1" maximum="10" change="updateLineExample();" value="@{lineThickness}" width="80" liveDragging="true"/>
<s:SpriteVisualElement width="40" height="20" id="lineExample"/>
<s:Button click="drawSprite.graphics.clear();" label="Clear all" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Annotation</s:Label>
<s:HGroup verticalAlign="middle">
<s:Label>Click an annotation to edit it, or </s:Label>
<custom:SmallButton label="Create a new annotation" icon="@Embed(../lib/bubble_add.png)" click="openAnnotation();"/>
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<s:VGroup width="100%" height="44" top="4">
<s:Label fontSize="18" color="#333333">Selected: Cut</s:Label>
<s:HGroup>
<s:Label>Select an area to crop.</s:Label>
<s:Button label="Crop selected area" />
</s:HGroup>
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</mx:HBox>
<mx:Canvas width="100%" height="100%" horizontalScrollPolicy="on" verticalScrollPolicy="on" rollOver="canvasOver();" rollOut="canvasOut();">
<mx:Box backgroundColor="#eeeeee" width="100%" height="100%" verticalAlign="middle" horizontalAlign="center" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Box id="drawArea" backgroundColor="#ffffff" width="300" height="200" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:ViewStack id="stack" change="stackChange();">
<s:NavigatorContent>
<custom:BigButton icon="@Embed(../lib/folder.png)" subtext="Import picture" toolTip="Open file" enabled="true" buttonMode="true" bWidth="300" bHeight="200" click="importPicture();" />
</s:NavigatorContent>
<s:NavigatorContent>
<mx:Image id="imgHolder" />
<s:Group id="drawGroup">
<s:Group id="drawSprite" />
<s:Group id="tempSprite" />
</s:Group>
<s:Group id="annotations">
</s:Group>
<mx:Box id="drawHitArea" alpha="0" backgroundColor="#000000" mouseDown="drawDown();" mouseMove="drawMove();"/>
</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</mx:Box>
</mx:Canvas>
</s:VGroup>

</s:WindowedApplication>

Thanks for reading!
Read more »