Showing posts with label creating. Show all posts
Showing posts with label creating. Show all posts
Wednesday, February 11, 2015
Creating “HelloAndroid” project using android studio
To create a project in android studio please follow the following steps.
1. Run android studio

2. Create a new project

3. Choose options for basic settings

4. Choose activity type for default activity

5. Set name for default activity

6. System will create project now

7. Navigate to layout folder according to below image

8. Double click on activity_hellow_activity.xml file
9. Run application
10. System will display output

Thursday, February 5, 2015
AS3 Countdown Timer Creating a Simple Countdown Timer in Flash Using ActionScript 3
In this tutorial, well learn how to create a very simple AS3 countdown timer in Flash.
Step 1
Create a new Flash ActionScript 3 document. Select the text tool once your document opens up.
Step 2
Go to the Properties Inspector and choose the following options:
Step 3
Then click on the stage to add the text field. Make sure that the text field is big enough for whatever font size you chose.
Step 4
Make sure that your text field is selected, then go back to the Properties Inspector and give it an instance name. Lets name this timer_txt.

Step 5
Create a new layer for the actions, then select the first keyframe of this new layer, and open up the Actions panel.
Step 6
In the script pane of the Actions panel, create a number variable. Lets initialize it with a value of 10.
Step 7
Assign the nCount variable to the text property of the timer_txt text field.
Step 8
Test your movie. You should see the number displayed in the text field.
Step 9
Go back to the code, and this time, create a Timer object with a delay of 1000. And for the repeatCount parameter, lets pass the nCount variable. The delay is the first value we pass to the Timer() constructor. The repeatCount is the second value that we pass.
Step 10
Now lets create an event listener function for the TimerEvent.TIMER event. This event gets dispatched at a rate that depends on the delay that was specified. Since we specified a delay of 1000 milliseconds, then this means that the event listener function will get called every 1 second. Lets name this event listener function as countdown.
Step 11
In the event listener function, we want to subtract nCount by 1 so that each time the function gets called, nCount will decrease. And then well display the new value in the timer_txt text field again so that it gets updated.
Step 12
Lastly, lets make sure that we start the timer using the start() method of the Timer class. If we dont start the timer, then we wont see anything happen.
Step 13
Test the movie. You should now have a working countdown timer.
Read more »
Step 1
Create a new Flash ActionScript 3 document. Select the text tool once your document opens up.
Step 2
Go to the Properties Inspector and choose the following options:
- Text field type: Classic - Dynamic Text

- For the font family, you can choose either _sans, _serif, or _typewriter

- Anti-alias: Use device fonts

- For the text color, make sure that its different from the background color of your stage
- You can choose whatever font size that you want
Step 3
Then click on the stage to add the text field. Make sure that the text field is big enough for whatever font size you chose.
Step 4
Make sure that your text field is selected, then go back to the Properties Inspector and give it an instance name. Lets name this timer_txt.

Step 5
Create a new layer for the actions, then select the first keyframe of this new layer, and open up the Actions panel.
Step 6
In the script pane of the Actions panel, create a number variable. Lets initialize it with a value of 10.
var nCount:Number = 10;This will be our countdown number. We will be subtracting 1 from this number every 1 second in order to create the countdown effect.Step 7
Assign the nCount variable to the text property of the timer_txt text field.
var nCount:Number = 10;
timer_txt.text = nCount.toString();This will display the number in the text field. We have to use the toString() method so that the number gets converted into a string. It needs to be converted into a string so that it can be displayed as text.Step 8
Test your movie. You should see the number displayed in the text field.
Step 9
Go back to the code, and this time, create a Timer object with a delay of 1000. And for the repeatCount parameter, lets pass the nCount variable. The delay is the first value we pass to the Timer() constructor. The repeatCount is the second value that we pass.
var nCount:Number = 10;
var myTimer:Timer = new Timer(1000, nCount);
timer_txt.text = nCount.toString();This creates a new Timer object named myTimer with a delay of 1000. The 1000 stands for 1000 milliseconds. This means that the timer will count every 1000 milliseconds (or 1 second). For the repeatCount, its going to have the same value as the initial value of nCount. Since our nCount variable has a value of 10 when we pass it to the repeatCount parameter, then repeatCount will also be 10. This means that our timer will count 10 times and then stop.Step 10
Now lets create an event listener function for the TimerEvent.TIMER event. This event gets dispatched at a rate that depends on the delay that was specified. Since we specified a delay of 1000 milliseconds, then this means that the event listener function will get called every 1 second. Lets name this event listener function as countdown.
var nCount:Number = 10;
var myTimer:Timer = new Timer(1000, nCount);
timer_txt.text = nCount.toString();
myTimer.addEventListener(TimerEvent.TIMER, countdown);
function countdown(e:TimerEvent):void
{
}Step 11
In the event listener function, we want to subtract nCount by 1 so that each time the function gets called, nCount will decrease. And then well display the new value in the timer_txt text field again so that it gets updated.
var nCount:Number = 10;
var myTimer:Timer = new Timer(1000, nCount);
timer_txt.text = nCount.toString();
myTimer.addEventListener(TimerEvent.TIMER, countdown);
function countdown(e:TimerEvent):void
{
nCount--;
timer_txt.text = nCount.toString();
}Dont test the movie just yet. We still need to add one more line of code.Step 12
Lastly, lets make sure that we start the timer using the start() method of the Timer class. If we dont start the timer, then we wont see anything happen.
var nCount:Number = 10;
var myTimer:Timer = new Timer(1000, nCount);
timer_txt.text = nCount.toString();
myTimer.start();
myTimer.addEventListener(TimerEvent.TIMER, countdown);
function countdown(e:TimerEvent):void
{
nCount--;
timer_txt.text = nCount.toString();
}
Step 13
Test the movie. You should now have a working countdown timer.
Creating EasyTooltip class Part 4
Today we will make a simple mouse follower, which displays very simple tool tips.
Before we move on to adding new code, lets go to TooltipListener class and set tip and list variables public instead of private:
Now lets create a new class called TooltipCursor. I want to make this the only object thats displayed in Tooltip - the content of it changes depending on what tooltip is currently displayed, but the object itself is the same.
Create TooltipCursor.as class, which extends MvoieClip. Add "txt" variable, which is a TextField, add it to the display list:
We can now go to EasyTooltip.as and declare an instance of this new class:
In the constructor, set its mouseChildren and mouseEnabled properties to false, add it to the parents display list and also add an ENTER_FRAME event listener for the cursor.
In the event handler, we loop through all listeners to see which ones "display" property is true. If none is displayed, set cursors alpha to 0. If one of the listeners display value is true - set the cursors alpha to 1, and update its text fields text value and width:
Full EasyTooltip class so far:
Now when you roll over a listener, you can see a very simple tooltip appearing, and disappearing as you roll out.
Thanks for reading!
Read more »
Before we move on to adding new code, lets go to TooltipListener class and set tip and list variables public instead of private:
package com.kircode.EasyTooltip
{
import flash.display.DisplayObject;
import flash.events.MouseEvent;
/**
* Manages a single visible object that displays a tooltip when rolled over.
* @author Kirill Poletaev
*/
public class TooltipListener
{
public var tip:Tooltip;
public var list:DisplayObject;
public function TooltipListener(listener:DisplayObject, tooltip:Tooltip)
{
list = listener;
tip = tooltip;
listener.addEventListener(MouseEvent.ROLL_OVER, onOver);
listener.addEventListener(MouseEvent.ROLL_OUT, onOut);
}
public function kill():void {
list.removeEventListener(MouseEvent.ROLL_OVER, onOver);
list.removeEventListener(MouseEvent.ROLL_OUT, onOut);
}
private function onOver(evt:MouseEvent):void {
tip.display = true;
}
private function onOut(evt:MouseEvent):void {
tip.display = false;
}
}
}
Now lets create a new class called TooltipCursor. I want to make this the only object thats displayed in Tooltip - the content of it changes depending on what tooltip is currently displayed, but the object itself is the same.
Create TooltipCursor.as class, which extends MvoieClip. Add "txt" variable, which is a TextField, add it to the display list:
package com.kircode.EasyTooltip
{
import flash.display.MovieClip;
import flash.text.TextField;
/**
* The cursor that follows the mouse (if not set otherwise) and displays each tooltip.
* @author Kirill Poletaev
*/
public class TooltipCursor extends MovieClip
{
public var txt:TextField;
public function TooltipCursor()
{
txt = new TextField();
addChild(txt);
txt.y = - 20;
}
}
}
We can now go to EasyTooltip.as and declare an instance of this new class:
private var cursor:TooltipCursor;
In the constructor, set its mouseChildren and mouseEnabled properties to false, add it to the parents display list and also add an ENTER_FRAME event listener for the cursor.
cursor = new TooltipCursor();
cursor.mouseChildren = false;
cursor.mouseEnabled = false;
parent.addChild(cursor);
cursor.addEventListener(Event.ENTER_FRAME, onFrame);
In the event handler, we loop through all listeners to see which ones "display" property is true. If none is displayed, set cursors alpha to 0. If one of the listeners display value is true - set the cursors alpha to 1, and update its text fields text value and width:
private function onFrame(evt:Event):void {
var displayInd:int = -1;
for (var i:int = 0; i < listeners.length; i++) {
if (listeners[i].tip.display) {
displayInd = i;
break;
}
}
if (displayInd == -1) {
cursor.alpha = 0;
}else {
cursor.alpha = 1;
cursor.txt.text = listeners[displayInd].tip.msg;
cursor.txt.width = cursor.txt.textWidth + 10;
}
cursor.x = par.mouseX;
cursor.y = par.mouseY;
}
Full EasyTooltip class so far:
package com.kircode.EasyTooltip
{
import flash.display.DisplayObjectContainer;
import flash.display.DisplayObject;
import flash.events.Event;
/**
* Utility for creation of tooltips.
* @author Kirill Poletaev
*/
public class EasyTooltip
{
private var par:DisplayObject;
public var listeners:Array;
private var cursor:TooltipCursor;
/**
* Create a Tooltip manager. Needed for creating and managing tooltips.
* @paramparent Reference to the parent of tooltips - the container, which will contain the objects that will be rolled over.
*/
public function EasyTooltip(parent:DisplayObjectContainer)
{
par = parent;
listeners = [];
cursor = new TooltipCursor();
cursor.mouseChildren = false;
cursor.mouseEnabled = false;
parent.addChild(cursor);
cursor.addEventListener(Event.ENTER_FRAME, onFrame);
trace("Tooltip manager created!");
}
/**
* Add a Tooltip listener.
* @paramlistener Object, which invokes the tooltip on roll over.
* @paramtooltip Message to be displayed.
* @returnNewly created Tooltip object. Can be used to dynamically change its properties in real-time.
*/
public function addListener(listener:DisplayObject, tooltip:String):Tooltip {
var tip:Tooltip = new Tooltip(tooltip);
var list:TooltipListener = new TooltipListener(listener, tip);
listeners.push(list);
return tip;
}
private function onFrame(evt:Event):void {
var displayInd:int = -1;
for (var i:int = 0; i < listeners.length; i++) {
if (listeners[i].tip.display) {
displayInd = i;
break;
}
}
if (displayInd == -1) {
cursor.alpha = 0;
}else {
cursor.alpha = 1;
cursor.txt.text = listeners[displayInd].tip.msg;
cursor.txt.width = cursor.txt.textWidth + 10;
}
cursor.x = par.mouseX;
cursor.y = par.mouseY;
}
}
}
Now when you roll over a listener, you can see a very simple tooltip appearing, and disappearing as you roll out.
Thanks for reading!
Creating Advanced Alert Window class Part 34
Today well add the ability to reset the constructor options of an AdvAlertManager object in real-time, as well as add support for setting default width, height and minHeight values of an alert window.
Firstly, go to AdvAlertManager and declare 3 new variables for holding default width, height and minheigh values:
Now go to the constructor and add 3 new parameters - defaultWidth, defaultHeight and defaultMinHeight.
Inside the constructor, apply these values to defWidth, defHeight and defMinHeight.
Also, remove the if..statement that checks if tabDisable is true before adding keyboard listener.
The if...statement was taken away because well add an option to change the value of tabDisable variable. Instead of deciding whether to add a keyboard listener based on the value, well always add the keyboard event, but check if tabDisable is true inside the kDown function before executing the code:
Now go to alert() function and add 3 new lines that check if width, height or minHeight values are 0, and if so - apply default values to them:
Full alert() function:
Now create a new public function called reset(), with the same parameters as the constructor. The contents are the same as in the constructor too, with the exception of 2 lines - exclude the ones that set windows array to a new array and the one that adds keyboard listener.
Full AdvAlertManager class:
Heres an example of using both of the new features in main.as:
Thanks for reading!
Read more »
Firstly, go to AdvAlertManager and declare 3 new variables for holding default width, height and minheigh values:
private var defWidth:int;
private var defHeight:int;
private var defMinHeight:int;
Now go to the constructor and add 3 new parameters - defaultWidth, defaultHeight and defaultMinHeight.
Inside the constructor, apply these values to defWidth, defHeight and defMinHeight.
Also, remove the if..statement that checks if tabDisable is true before adding keyboard listener.
/**
* AdvAlertManager constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramstage Stage reference.
* @paramwindowSkin Default skin for alert windows.
* @parambuttonSkin Default skin for buttons in this window.
* @paramopenSound Default window open sound.
* @paramdisableTab Disable tab focusing on the parent container when an alert window is visible. This also enables tab navigation for the alert windows buttons.
* @paramdefaultWidth Set default width for all window alerts. 300 by default.
* @paramdefaultHeight Set default height for all window alerts. 0 by default. If 0, the window is auto-sized based on the text length.
* @paramdefaultMinHeight Set default minimal height for all window alerts. 0 by default.
*/
public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, stage:Stage, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, openSound:Sound = null, disableTab:Boolean = true, defaultWidth:int = 300, defaultHeight:int = 0, defaultMinHeight:int = 0)
{
defWidth = defaultWidth;
defHeight = defaultHeight;
defMinHeight = defaultMinHeight;
openingSound = openSound;
skin = windowSkin;
bSkin = buttonSkin;
tabDisable = disableTab;
if (skin == null) skin = new AdvAlertSkin();
if (bSkin == null) bSkin = new AdvAlertButtonSkin();
defaultContainer = defaultWindowContainer;
pWidth = stage.stageWidth;
pHeight = stage.stageHeight;
stg = stage;
windows = [];
stg.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
}
The if...statement was taken away because well add an option to change the value of tabDisable variable. Instead of deciding whether to add a keyboard listener based on the value, well always add the keyboard event, but check if tabDisable is true inside the kDown function before executing the code:
private function kDown(evt:KeyboardEvent):void {
if(tabDisable){
if (evt.keyCode == 9 && topWindow!=null && topWindow.buttons.length>0) {
focusedButton++;
if (focusedButton == topWindow.buttons.length) {
focusedButton = 0;
}
for (var i:int = 0; i < topWindow.buttons.length; i++) {
topWindow.buttons[i].over = false;
topWindow.buttons[i].updateDraw();
}
topWindow.buttons[focusedButton].over = true;
topWindow.buttons[focusedButton].updateDraw();
}
if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
if (focusedButton == -1) {
focusedButton = 0;
}
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
closeWindow(currentIndex, closeHandler);
}
}
}
Now go to alert() function and add 3 new lines that check if width, height or minHeight values are 0, and if so - apply default values to them:
if (width == 0) width = defWidth;
if (height == 0) height = defHeight;
if (minHeight == 0) minHeight = defMinHeight;
Full alert() function:
/**
* Create an alert window.
* @paramtext Text value of the alert window.
* @paramtitle Title value of the alert window.
* @parambuttons (Optional) Array of AdvAlertButton objects that represent buttons in the alert window.
* @paramcloseHandler (Optional) Close handling function. Must receive a string value as parameter (which will hold the label of the button that was clicked).
* @paramwidth (Optional) Width of the alert window. If 0, set to default.
* @paramheight (Optional) Height of the alert window. If 0, set to default. If default is 0, window will be auto sized.
* @paramposition (Optional) Coordinates of top-left corner of the alert window. If not specified - the window is centered.
* @paramminHeight (Optional) The minimum height value of the alert window. 0 by default.
*/
public function alert(text:String, title:String = "", buttons:Array = null, closeHandler:Function = null, width:int = 0, height:int = 0, position:Point = null, minHeight:int = 0, openSound:Sound = null):AdvAlertWindow {
if (width == 0) width = defWidth;
if (height == 0) height = defHeight;
if (minHeight == 0) minHeight = defMinHeight;
if (openSound != null) {
openSound.play();
}else
if (openingSound != null) {
openingSound.play();
}
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [new AdvAlertButton("OK")];
for (var i:int = buttons.length - 1; i >= 0; i--) {
if (!buttons[i] is AdvAlertButton) {
throw new Error("An item in buttons array is not an AdvAlertButton instance. Ignoring...");
buttons.splice(i, 1);
}else {
buttons[i].addEventListener(MouseEvent.CLICK, buttonHandler);
}
}
var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons, bSkin, pWidth, pHeight, minHeight);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
var currentIndex:int = windows.length;
windows.push( {window:w, blur:b, onclose:closeHandler} );
defaultContainer.addChild(b);
defaultContainer.addChild(w);
topWindow = w;
focusedButton = -1;
if (tabDisable) {
defaultContainer.tabChildren = false;
defaultContainer.tabEnabled = false;
}
function buttonHandler(evt:MouseEvent):void {
closeWindow(currentIndex, closeHandler);
}
return w;
}
Now create a new public function called reset(), with the same parameters as the constructor. The contents are the same as in the constructor too, with the exception of 2 lines - exclude the ones that set windows array to a new array and the one that adds keyboard listener.
/**
* Set new options for the alert manager object - has the same parameters as the constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramstage Stage reference.
* @paramwindowSkin Default skin for alert windows.
* @parambuttonSkin Default skin for buttons in this window.
* @paramopenSound Default window open sound.
* @paramdisableTab Disable tab focusing on the parent container when an alert window is visible. This also enables tab navigation for the alert windows buttons.
* @paramdefaultWidth Set default width for all window alerts. 300 by default.
* @paramdefaultHeight Set default height for all window alerts. 0 by default. If 0, the window is auto-sized based on the text length.
* @paramdefaultMinHeight Set default minimal height for all window alerts. 0 by default.
*/
public function reset(defaultWindowContainer:DisplayObjectContainer, stage:Stage, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, openSound:Sound = null, disableTab:Boolean = true, defaultWidth:int = 300, defaultHeight:int = 0, defaultMinHeight:int = 0)
{
defWidth = defaultWidth;
defHeight = defaultHeight;
defMinHeight = defaultMinHeight;
openingSound = openSound;
skin = windowSkin;
bSkin = buttonSkin;
tabDisable = disableTab;
if (skin == null) skin = new AdvAlertSkin();
if (bSkin == null) bSkin = new AdvAlertButtonSkin();
defaultContainer = defaultWindowContainer;
pWidth = stage.stageWidth;
pHeight = stage.stageHeight;
stg = stage;
}
Full AdvAlertManager class:
package com.kircode.AdvAlert
{
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.media.Sound;
/**
* Advanced Alert window manager.
* @author Kirill Poletaev
*/
public class AdvAlertManager
{
private var windows:Array;
private var defaultContainer:DisplayObjectContainer;
private var pWidth:int;
private var pHeight:int;
private var skin:AdvAlertSkin;
private var bSkin:AdvAlertButtonSkin;
private var tabDisable:Boolean;
private var topWindow:AdvAlertWindow;
private var focusedButton:int;
private var stg:Stage;
private var openingSound:Sound;
private var defWidth:int;
private var defHeight:int;
private var defMinHeight:int;
/**
* AdvAlertManager constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramstage Stage reference.
* @paramwindowSkin Default skin for alert windows.
* @parambuttonSkin Default skin for buttons in this window.
* @paramopenSound Default window open sound.
* @paramdisableTab Disable tab focusing on the parent container when an alert window is visible. This also enables tab navigation for the alert windows buttons.
* @paramdefaultWidth Set default width for all window alerts. 300 by default.
* @paramdefaultHeight Set default height for all window alerts. 0 by default. If 0, the window is auto-sized based on the text length.
* @paramdefaultMinHeight Set default minimal height for all window alerts. 0 by default.
*/
public function AdvAlertManager(defaultWindowContainer:DisplayObjectContainer, stage:Stage, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, openSound:Sound = null, disableTab:Boolean = true, defaultWidth:int = 300, defaultHeight:int = 0, defaultMinHeight:int = 0)
{
defWidth = defaultWidth;
defHeight = defaultHeight;
defMinHeight = defaultMinHeight;
openingSound = openSound;
skin = windowSkin;
bSkin = buttonSkin;
tabDisable = disableTab;
if (skin == null) skin = new AdvAlertSkin();
if (bSkin == null) bSkin = new AdvAlertButtonSkin();
defaultContainer = defaultWindowContainer;
pWidth = stage.stageWidth;
pHeight = stage.stageHeight;
stg = stage;
windows = [];
stg.addEventListener(KeyboardEvent.KEY_DOWN, kDown);
}
private function kDown(evt:KeyboardEvent):void {
if(tabDisable){
if (evt.keyCode == 9 && topWindow!=null && topWindow.buttons.length>0) {
focusedButton++;
if (focusedButton == topWindow.buttons.length) {
focusedButton = 0;
}
for (var i:int = 0; i < topWindow.buttons.length; i++) {
topWindow.buttons[i].over = false;
topWindow.buttons[i].updateDraw();
}
topWindow.buttons[focusedButton].over = true;
topWindow.buttons[focusedButton].updateDraw();
}
if (evt.keyCode == 13 && topWindow != null && topWindow.buttons.length > 0) {
if (focusedButton == -1) {
focusedButton = 0;
}
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
closeWindow(currentIndex, closeHandler);
}
}
}
/**
* Close the top alert window.
*/
public function closeLast():void {
var currentIndex:int = windows.length - 1;
var closeHandler:Function = windows[currentIndex].onclose;
closeWindow(currentIndex, closeHandler);
}
private function closeWindow(currentIndex:int, closeHandler:Function):void {
if (closeHandler != null) closeHandler.call(topWindow.buttons[focusedButton], topWindow.buttons[focusedButton].txt);
windows[currentIndex].window.parent.removeChild(windows[currentIndex].window);
windows[currentIndex].blur.parent.removeChild(windows[currentIndex].blur);
windows.splice(currentIndex, 1);
focusedButton = -1;
if (windows.length > 0) topWindow = windows[windows.length - 1].window;
if (windows.length == 0) topWindow = null;
if (tabDisable && windows.length==0) {
defaultContainer.tabChildren = true;
defaultContainer.tabEnabled = true;
}
}
/**
* Set skin of all future created alert windows.
* @paramwindowSkin Reference to an AdvAlertSkin object.
*/
public function setSkin(windowSkin:AdvAlertSkin):void {
skin = windowSkin;
}
/**
* Set skin of all buttons of future created alert windows.
* @parambuttonSkin Reference to an AdvAlertButtonSkin object.
*/
public function setButtonSkin(buttonSkin:AdvAlertButtonSkin):void {
bSkin = buttonSkin;
}
/**
* Set new options for the alert manager object - has the same parameters as the constructor.
* @paramdefaultWindowContainer Parent container of alert windows.
* @paramstage Stage reference.
* @paramwindowSkin Default skin for alert windows.
* @parambuttonSkin Default skin for buttons in this window.
* @paramopenSound Default window open sound.
* @paramdisableTab Disable tab focusing on the parent container when an alert window is visible. This also enables tab navigation for the alert windows buttons.
* @paramdefaultWidth Set default width for all window alerts. 300 by default.
* @paramdefaultHeight Set default height for all window alerts. 0 by default. If 0, the window is auto-sized based on the text length.
* @paramdefaultMinHeight Set default minimal height for all window alerts. 0 by default.
*/
public function reset(defaultWindowContainer:DisplayObjectContainer, stage:Stage, windowSkin:AdvAlertSkin = null, buttonSkin:AdvAlertButtonSkin = null, openSound:Sound = null, disableTab:Boolean = true, defaultWidth:int = 300, defaultHeight:int = 0, defaultMinHeight:int = 0)
{
defWidth = defaultWidth;
defHeight = defaultHeight;
defMinHeight = defaultMinHeight;
openingSound = openSound;
skin = windowSkin;
bSkin = buttonSkin;
tabDisable = disableTab;
if (skin == null) skin = new AdvAlertSkin();
if (bSkin == null) bSkin = new AdvAlertButtonSkin();
defaultContainer = defaultWindowContainer;
pWidth = stage.stageWidth;
pHeight = stage.stageHeight;
stg = stage;
}
/**
* Create an alert window.
* @paramtext Text value of the alert window.
* @paramtitle Title value of the alert window.
* @parambuttons (Optional) Array of AdvAlertButton objects that represent buttons in the alert window.
* @paramcloseHandler (Optional) Close handling function. Must receive a string value as parameter (which will hold the label of the button that was clicked).
* @paramwidth (Optional) Width of the alert window. If 0, set to default.
* @paramheight (Optional) Height of the alert window. If 0, set to default. If default is 0, window will be auto sized.
* @paramposition (Optional) Coordinates of top-left corner of the alert window. If not specified - the window is centered.
* @paramminHeight (Optional) The minimum height value of the alert window. 0 by default.
*/
public function alert(text:String, title:String = "", buttons:Array = null, closeHandler:Function = null, width:int = 0, height:int = 0, position:Point = null, minHeight:int = 0, openSound:Sound = null):AdvAlertWindow {
if (width == 0) width = defWidth;
if (height == 0) height = defHeight;
if (minHeight == 0) minHeight = defMinHeight;
if (openSound != null) {
openSound.play();
}else
if (openingSound != null) {
openingSound.play();
}
if (position == null) position = new Point((pWidth / 2) - (width / 2), (pHeight / 2) - (height / 2));
if (buttons == null) buttons = [new AdvAlertButton("OK")];
for (var i:int = buttons.length - 1; i >= 0; i--) {
if (!buttons[i] is AdvAlertButton) {
throw new Error("An item in buttons array is not an AdvAlertButton instance. Ignoring...");
buttons.splice(i, 1);
}else {
buttons[i].addEventListener(MouseEvent.CLICK, buttonHandler);
}
}
var w:AdvAlertWindow = new AdvAlertWindow(text, title, width, height, position, skin, buttons, bSkin, pWidth, pHeight, minHeight);
var b:AdvAlertBlur = new AdvAlertBlur(pWidth, pHeight, skin);
var currentIndex:int = windows.length;
windows.push( {window:w, blur:b, onclose:closeHandler} );
defaultContainer.addChild(b);
defaultContainer.addChild(w);
topWindow = w;
focusedButton = -1;
if (tabDisable) {
defaultContainer.tabChildren = false;
defaultContainer.tabEnabled = false;
}
function buttonHandler(evt:MouseEvent):void {
closeWindow(currentIndex, closeHandler);
}
return w;
}
}
}
Heres an example of using both of the new features in main.as:
var mySkin:AdvAlertSkin = new AdvAlertSkin();
mySkin.addContent(warning_icon, new Point(5, 40));
mySkin.textPadding.left = 140;
mySkin.titleFilters = [new DropShadowFilter(0, 0, 0, 1, 3, 3, 2, 3)];
mySkin.textFilters = [new DropShadowFilter(1, 45, 0, 0.4, 0, 0, 5, 1)];
var myButtonSkin:AdvAlertButtonSkin = new AdvAlertButtonSkin();
myButtonSkin.textFilters = [new DropShadowFilter(1, 45, 0, 0.4, 0, 0, 5, 1)];
myButtonSkin.hover_textFilters = [new DropShadowFilter(1, 45, 0, 0.4, 0, 0, 5, 1), new DropShadowFilter(0, 0, 0, 1, 3, 3, 1, 3)];
AlertManager = new AdvAlertManager(this, stage);
AlertManager.alert("Alert window with default settings.", "Example alert!");
AlertManager.reset(this, stage, mySkin, myButtonSkin, null, true, 400, 0, 160);
AlertManager.alert("Alert window with custom settings.", "Example alert!");
Thanks for reading!
Wednesday, February 4, 2015
Creating a Flex AIR text editor Part 40
In todays part we will add a check which prevents the user from opening the same document more than once, as well as add the Save All feature.
Go to the doOpen() function. Inside of it we have an internal function called fileLoad which is called once the user selects the file on their hard drive. Here we can check if this file is already open and if it is, throw a warning message instead of executing the code. Add a check with a function fileDuplicateCheck(file.nativePath) as the conditional.
This fileDuplicateCheck obviously returns a boolean value - true if this file has no duplicates, false of this file is already open.
The check is done using a simple loop:
As you can see, we also select the tab of that file if a match was found.
Now lets add the save all functionality.
First go to the doSave() function and add a conditional which checks if the current tab has its saved property false. Only execute the code if the saved property is false (theres no need in saving a file with no changes made!).
Now create a function called doSaveAll(), which calls doSave() on each index using a loop.
Now go to the tool bar section and find your save all button, set its click property to the doSaveAll function:
Add a Save All menu item to the XML:
And update the menuSelect function so that it calls doSaveAll() if the selected menu item was Save All.
Full code:
Everything works OK, except for the fact that if there are multiple unsaved tabs that have no location yet (Undefined ones) and you use Save All, the code throws an error. This is due to the fact that multiple browse dialog windows are called out at the same time.
We will work on this in the next part.
Thanks for reading!
Read more »
Go to the doOpen() function. Inside of it we have an internal function called fileLoad which is called once the user selects the file on their hard drive. Here we can check if this file is already open and if it is, throw a warning message instead of executing the code. Add a check with a function fileDuplicateCheck(file.nativePath) as the conditional.
private function doOpen():void {
var file:File = new File();
file.browseForOpen("Open document", [new FileFilter("Text documents", "*.txt"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, fileLoad);
function fileLoad(evt:Event):void {
if(fileDuplicateCheck(file.nativePath)){
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ);
var str:String = stream.readUTFBytes(stream.bytesAvailable);
stream.close();
str = str.replace(File.lineEnding, "
");
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + file.name + " opened";
updateStatus();
tabData.addItem( { title:file.name, textData: str, saved:true, location:file.nativePath} );
tabSelectedIndex = tabData.length - 1;
tabChange();
updateTextSize();
}else {
Alert.show("File " + file.name + " is already open","Error");
}
}
}
This fileDuplicateCheck obviously returns a boolean value - true if this file has no duplicates, false of this file is already open.
The check is done using a simple loop:
private function fileDuplicateCheck(loc:String):Boolean {
var toReturn:Boolean = true;
for (var i:int = 0; i < tabData.length; i++) {
if (tabData[i].location == loc) {
toReturn = false;
tabSelectedIndex = i;
tabChange();
break;
}
}
return toReturn;
}
As you can see, we also select the tab of that file if a match was found.
Now lets add the save all functionality.
First go to the doSave() function and add a conditional which checks if the current tab has its saved property false. Only execute the code if the saved property is false (theres no need in saving a file with no changes made!).
private function doSave(ind:int, updateIndexNeeded:Boolean = true):void {
if(!tabData[ind].saved){
if (tabData[ind].location != "") {
saveFile(textArea.text, tabData[ind].location, updateIndexNeeded);
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + tabData[ind].title + " saved";
updateStatus();
tabData[ind].saved = true;
}else{
doSaveAs(textArea.text, ind, updateIndexNeeded);
}
}
}
Now create a function called doSaveAll(), which calls doSave() on each index using a loop.
private function doSaveAll():void {
for (var i:int = 0; i < tabData.length; i++) {
doSave(i);
}
}
Now go to the tool bar section and find your save all button, set its click property to the doSaveAll function:
<custom:IconButton icon="@Embed(../lib/disk_multiple.png)" toolTip="Save all" click="doSaveAll();" />
Add a Save All menu item to the XML:
<fx:XML id="windowMenu">
<root>
<menuitem label="File">
<menuitem label="New" key="n" controlKey="true" />
<menuitem label="Open" key="o" controlKey="true" />
<menuitem label="Save" key="s" controlKey="true" />
<menuitem label="Save As" key="s" controlKey="true" shiftKey="true" />
<menuitem label="Save All" key="s" controlKey="true" altKey="true" />
<menuitem type="separator"/>
<menuitem label="Print" key="p" controlKey="true" />
</menuitem>
<menuitem label="Edit">
<menuitem label="Undo" key="z" controlKey="true" enabled="{canUndo}" />
<menuitem label="Redo" key="y" controlKey="true" enabled="{canRedo}" />
<menuitem type="separator"/>
<menuitem label="Cut" key="x" controlKey="true" />
<menuitem label="Copy" key="c" controlKey="true" />
<menuitem label="Paste" key="v" controlKey="true" />
<menuitem type="separator"/>
<menuitem label="Select all" key="a" controlKey="true" />
</menuitem>
<menuitem label="Settings">
<menuitem label="Word wrap" type="check" toggled="{pref_wrap}" />
<menuitem label="Font..."/>
</menuitem>
<menuitem label="View">
<menuitem label="Tool bar" type="check" toggled="{pref_toolbar}" />
<menuitem label="Status bar" type="check" toggled="{pref_status}" />
<menuitem label="Line count" type="check" toggled="{pref_linecount}" />
<menuitem label="Side pane" type="check" toggled="{pref_sidepane}" />
</menuitem>
</root>
</fx:XML>
And update the menuSelect function so that it calls doSaveAll() if the selected menu item was Save All.
private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(void);
(evt.item.@label == "Open")?(doOpen()):(void);
(evt.item.@label == "Save")?(doSave(tabSelectedIndex)):(void);
(evt.item.@label == "Save As")?(doSaveAs(textArea.text, tabSelectedIndex)):(void);
(evt.item.@label == "Save All")?(doSaveAll()):(void);
(evt.item.@label == "Word wrap")?(pref_wrap = !pref_wrap):(void);
(evt.item.@label == "Cut")?(doCut()):(void);
(evt.item.@label == "Copy")?(doCopy()):(void);
(evt.item.@label == "Paste")?(doPaste()):(void);
(evt.item.@label == "Select all")?(doSelectall()):(void);
(evt.item.@label == "Status bar")?(pref_status = !pref_status):(void);
(evt.item.@label == "Tool bar")?(pref_toolbar = !pref_toolbar):(void);
(evt.item.@label == "Side pane")?(pref_sidepane = !pref_sidepane):(void);
(evt.item.@label == "Line count")?(pref_linecount = !pref_linecount):(void);
(evt.item.@label == "Font...")?(doFont()):(void);
(evt.item.@label == "Print")?(doPrint()):(void);
(evt.item.@label == "Undo")?(doUndo()):(void);
(evt.item.@label == "Redo")?(doRedo()):(void);
savePreferences();
updateStatus();
if (pref_wrap) {
pref_linecount = false;
}
updateTextSize();
countLines();
}
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="*"
creationComplete="init();" title="Kirpad" showStatusBar="{pref_status}"
minWidth="400" minHeight="200" height="700" width="900">
<s:menu>
<mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" />
</s:menu>
<fx:Script>
<![CDATA[
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.NativeWindowBoundsEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.net.SharedObject;
import flashx.textLayout.accessibility.TextAccImpl;
import flashx.textLayout.edit.EditManager;
import flashx.textLayout.edit.TextScrap;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.controls.TextArea;
import mx.events.FlexNativeMenuEvent;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.elements.Configuration;
import flash.system.System;
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import flash.ui.Mouse;
import mx.events.CloseEvent;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.events.ContextMenuEvent;
import mx.events.ResizeEvent;
import mx.core.FlexGlobals;
import mx.printing.FlexPrintJob;
import mx.printing.FlexPrintJobScaleType;
import flashx.undo.UndoManager;
import flashx.textLayout.operations.UndoOperation;
private var preferences:SharedObject = SharedObject.getLocal("kirpadPreferences");
[Bindable]
private var pref_wrap:Boolean = true;
[Bindable]
private var pref_status:Boolean = true;
[Bindable]
private var pref_toolbar:Boolean = true;
[Bindable]
private var pref_sidepane:Boolean = true;
[Bindable]
private var pref_linecount:Boolean = true;
[Bindable]
public var pref_fontsettings:Object = new Object();
private var initHeight:Number;
private var heightFixed:Boolean = false;
private var statusMessage:String;
[Bindable]
private var textHeight:Number;
[Bindable]
private var textWidth:Number;
[Bindable]
private var textY:Number;
[Bindable]
private var textX:Number;
[Bindable]
private var tabY:Number;
[Bindable]
private var sidePaneY:Number;
[Bindable]
private var sidePaneX:Number;
[Bindable]
private var sidePaneHeight:Number;
[Bindable]
private var sidePaneWidth:Number = 180;
[Bindable]
private var sideContentWidth:Number = 170;
[Bindable]
private var tabWidth:Number;
[Bindable]
private var lineCountWidth:Number = 40;
[Bindable]
private var lineNumbers:String = "1";
[Bindable]
private var lineDisplayedNum:int = 1;
[Bindable]
private var tabSelectedIndex:int = 0;
[Bindable]
private var canUndo:Boolean = false;
[Bindable]
private var canRedo:Boolean = false;
private var previousTextInOperation:String = "";
private var currentTextInOperation:String = "";
private var previousIndex:int = 0;
private var rightclickTabIndex:int = 0;
private var untitledNum:int = 0;
private var tabsToClose:int = 0;
private var closeAfterConfirm:Boolean = false;
public var fontWindow:FontWindow = new FontWindow();
private var undoManager:UndoManager;
private var editManager:EditManager;
private var saveWait:Boolean = false;
private function init():void {
// Create a listener for every frame
addEventListener(Event.ENTER_FRAME, everyFrame);
// Set initHeight to the initial height value on start
initHeight = height;
// Set preferences if loaded for the first time
if (preferences.data.firsttime == null) {
preferences.data.firsttime = true;
preferences.data.wrap = false;
preferences.data.status = true;
preferences.data.toolbar = true;
preferences.data.sidepane = true;
preferences.data.linecount = true;
preferences.data.fontsettings = {fontfamily:"Lucida Console", fontsize:14, fontstyle:"normal", fontweight:"normal", fontcolor:0x000000, bgcolor:0xffffff};
preferences.flush();
}
// Set preferences loaded from local storage
pref_wrap = preferences.data.wrap;
pref_status = preferences.data.status;
pref_toolbar = preferences.data.toolbar;
pref_sidepane = preferences.data.sidepane;
pref_fontsettings = preferences.data.fontsettings;
pref_linecount = preferences.data.linecount;
// Allow insertion of tabs
var textFlow:TextFlow = textArea.textFlow;
var config:Configuration = Configuration(textFlow.configuration);
config.manageTabKey = true;
// Set status message
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Kirpad initialized";
updateStatus();
// Close all sub-windows if main window is closed
addEventListener(Event.CLOSING, onClose);
// Add listener for the event that is dispatched when new font settings are applied
fontWindow.addEventListener(Event.CHANGE, fontChange);
// Update real fonts with the data from the settings values
updateFonts();
// Create a listener for resizing
addEventListener(NativeWindowBoundsEvent.RESIZE, onResize);
// Context menu declaration for the tabbar control
var cm_close:ContextMenuItem = new ContextMenuItem("Close tab");
cm_close.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextClose);
var cm_closeother:ContextMenuItem = new ContextMenuItem("Close other tabs");
cm_closeother.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, tabContextCloseOther);
var cm:ContextMenu = new ContextMenu();
cm.items = [cm_close, cm_closeother];
cm.hideBuiltInItems();
tabBar.contextMenu = cm;
tabBar.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, tabRightClick);
// Context menu declaration for the tab management list control
sideList.contextMenu = cm;
sideList.addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, listRightClick);
// Listen to keyboard
addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
// Undo management
undoManager = new UndoManager();
editManager = new EditManager(undoManager);
textArea.textFlow.interactionManager = editManager;
}
private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@label == "New")?(doNew()):(void);
(evt.item.@label == "Open")?(doOpen()):(void);
(evt.item.@label == "Save")?(doSave(tabSelectedIndex)):(void);
(evt.item.@label == "Save As")?(doSaveAs(textArea.text, tabSelectedIndex)):(void);
(evt.item.@label == "Save All")?(doSaveAll()):(void);
(evt.item.@label == "Word wrap")?(pref_wrap = !pref_wrap):(void);
(evt.item.@label == "Cut")?(doCut()):(void);
(evt.item.@label == "Copy")?(doCopy()):(void);
(evt.item.@label == "Paste")?(doPaste()):(void);
(evt.item.@label == "Select all")?(doSelectall()):(void);
(evt.item.@label == "Status bar")?(pref_status = !pref_status):(void);
(evt.item.@label == "Tool bar")?(pref_toolbar = !pref_toolbar):(void);
(evt.item.@label == "Side pane")?(pref_sidepane = !pref_sidepane):(void);
(evt.item.@label == "Line count")?(pref_linecount = !pref_linecount):(void);
(evt.item.@label == "Font...")?(doFont()):(void);
(evt.item.@label == "Print")?(doPrint()):(void);
(evt.item.@label == "Undo")?(doUndo()):(void);
(evt.item.@label == "Redo")?(doRedo()):(void);
savePreferences();
updateStatus();
if (pref_wrap) {
pref_linecount = false;
}
updateTextSize();
countLines();
}
private function savePreferences():void {
preferences.data.wrap = pref_wrap;
preferences.data.status = pref_status;
preferences.data.toolbar = pref_toolbar;
preferences.data.fontsettings = pref_fontsettings;
preferences.data.sidepane = pref_sidepane;
preferences.data.linecount = pref_linecount;
preferences.flush();
}
private function doCut():void {
var selectedText:String = textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
System.setClipboard(selectedText);
insertText("");
}
private function doCopy():void {
var selectedText:String = textArea.text.substring(textArea.selectionActivePosition, textArea.selectionAnchorPosition);
System.setClipboard(selectedText);
}
private function doPaste():void{
var myClip:Clipboard = Clipboard.generalClipboard;
var pastedText:String = myClip.getData(ClipboardFormats.TEXT_FORMAT) as String;
insertText(pastedText);
}
private function doSelectall():void {
textArea.selectAll();
}
private function insertText(str:String):void {
editManager.insertText(str);
}
private function cursorFix():void{
Mouse.cursor = "ibeam";
}
private function everyFrame(evt:Event):void {
if (!heightFixed && height==initHeight) {
height = initHeight - 20;
if (height != initHeight) {
heightFixed = true;
updateTextSize();
}
}
updateLineScroll();
if (sideList.selectedIndices.length==0) {
sideList.selectedIndex = tabSelectedIndex;
}
}
private function onResize(evt:ResizeEvent):void {
updateTextSize();
}
private function updateTextSize():void {
tabY = (toolBar.visible)?(toolBar.height):(0);
textX = (pref_linecount)?(lineCountWidth):(0);
var statusHeight:Number = (pref_status)?(statusBar.height):(0);
textWidth = (pref_sidepane)?(width - sidePaneWidth - textX):(width - textX);
tabWidth = textWidth + textX;
var tabbarScrollHeight:Number = (tabData.length * 170 > tabWidth)?(15):(0);
textY = tabBar.height + tabY + tabbarScrollHeight;
textHeight = height - textY - statusHeight;
focusManager.setFocus(textArea);
sidePaneHeight = textHeight + tabBar.height + tabbarScrollHeight;
sidePaneY = textY - tabBar.height - tabbarScrollHeight;
sidePaneX = width - sidePaneWidth;
}
private function updateStatus():void {
var str:String = new String();
str = (pref_wrap)?("Word wrapping on"):(caretPosition());
status = str + " " + statusMessage;
}
private function caretPosition():String {
var pos:int = textArea.selectionActivePosition;
var str:String = textArea.text.substring(0, pos);
var lines:Array = str.split("
");
var line:int = lines.length;
var col:int = lines[lines.length - 1].length + 1;
return "Ln " + line + ", Col " + col;
}
private function doFont():void{
fontWindow.open();
fontWindow.activate();
fontWindow.visible = true;
fontWindow.setValues(pref_fontsettings.fontsize, pref_fontsettings.fontfamily, pref_fontsettings.fontstyle, pref_fontsettings.fontweight, pref_fontsettings.fontcolor, pref_fontsettings.bgcolor);
}
private function onClose(evt:Event):void {
if(!closeAfterConfirm){
evt.preventDefault();
var allWindows:Array = NativeApplication.nativeApplication.openedWindows;
for (var i:int = 1; i < allWindows.length; i++)
{
allWindows[i].close();
}
// Check if there are any unsaved tabs
var needSaving:Boolean = false;
tabsToClose = 0;
for (var u:int = 0; u < tabData.length; u++) {
if (tabData[u].saved == false) {
needSaving = true;
tabsToClose++;
}
}
// If there are unsaved tabs, dont close window yet, set closeAfterConfirm to true and close all tabs
if (needSaving) {
closeAfterConfirm = true;
for (var t:int = 0; t < tabData.length; t++) {
closeTab(t);
}
}
if (!needSaving) {
FlexGlobals.topLevelApplication.close();
}
}
}
private function fontChange(evt:Event):void{
pref_fontsettings.fontfamily = fontWindow.fontCombo.selectedItem.fontName;
pref_fontsettings.fontsize = fontWindow.sizeStepper.value;
if (fontWindow.styleCombo.selectedIndex == 0) {
pref_fontsettings.fontstyle = "normal";
pref_fontsettings.fontweight = "normal";
}
if (fontWindow.styleCombo.selectedIndex == 1) {
pref_fontsettings.fontstyle = "italic";
pref_fontsettings.fontweight = "normal";
}
if (fontWindow.styleCombo.selectedIndex == 2) {
pref_fontsettings.fontstyle = "normal";
pref_fontsettings.fontweight = "bold";
}
if (fontWindow.styleCombo.selectedIndex == 3) {
pref_fontsettings.fontstyle = "italic";
pref_fontsettings.fontweight = "bold";
}
pref_fontsettings.fontcolor = fontWindow.colorPicker.selectedColor;
pref_fontsettings.bgcolor = fontWindow.bgColorPicker.selectedColor;
savePreferences();
updateFonts();
}
private function updateFonts():void{
textArea.setStyle("fontFamily", pref_fontsettings.fontfamily);
textArea.setStyle("fontSize", pref_fontsettings.fontsize);
textArea.setStyle("fontStyle", pref_fontsettings.fontstyle);
textArea.setStyle("fontWeight", pref_fontsettings.fontweight);
textArea.setStyle("color", pref_fontsettings.fontcolor);
textArea.setStyle("contentBackgroundColor", pref_fontsettings.bgcolor);
lineCount.setStyle("fontFamily", pref_fontsettings.fontfamily);
lineCount.setStyle("fontSize", pref_fontsettings.fontsize);
lineCount.setStyle("fontStyle", pref_fontsettings.fontstyle);
lineCount.setStyle("fontWeight", pref_fontsettings.fontweight);
lineCount.setStyle("color", pref_fontsettings.fontcolor);
lineCount.setStyle("contentBackgroundColor", pref_fontsettings.bgcolor);
}
private function onTabClose(evt:Event):void {
var tabWidth:Number = tabBar.width / tabData.length;
var cIndex:int = Math.floor(tabBar.mouseX / tabWidth);
tabSelectedIndex = cIndex;
tabChange();
closeTab(tabSelectedIndex);
}
private function onListClose(evt:Event):void {
tabSelectedIndex = sideList.selectedIndex;
tabChange();
closeTab(tabSelectedIndex);
}
private function closeTab(index:int):void {
if (tabData[index].saved) {
removeTab(index);
} else
if (!tabData[index].saved) {
Alert.show("Save " + tabData[index].title + " before closing?", "Confirmation", Alert.YES | Alert.NO, null, confirmClose);
}
function confirmClose(evt:CloseEvent):void {
tabsToClose--;
if (evt.detail == Alert.YES) {
tabSelectedIndex = index;
tabChange();
removeTab(index, true);
doSave(index, false);
}else {
removeTab(index);
}
}
}
private function removeTab(index:int, waitForSave:Boolean = false):void {
if(!closeAfterConfirm){
// if this is the last tab, create a new empty tab
if (tabData.length == 1) {
tabData.addItem( { title:"Untitled", textData:"", saved:false, location:""} );
}
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] Tab closed: " + tabData[index].title;
updateStatus();
tabData.removeItemAt(index);
tabSelectedIndex = tabBar.selectedIndex;
previousIndex = tabSelectedIndex;
textArea.text = tabData[tabSelectedIndex].textData;
textArea.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
}
if (closeAfterConfirm && tabsToClose == 0 && waitForSave == false) {
FlexGlobals.topLevelApplication.close();
}
if (waitForSave) {
saveWait = true;
}
countLines();
updateTextSize();
undoManager.clearAll();
textChange();
}
private function doNew():void {
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] New tab created";
updateStatus();
untitledNum++;
tabData.addItem( { title:"Untitled("+untitledNum+")", textData:"", saved:false, location:""} );
tabSelectedIndex = tabData.length - 1;
tabChange();
updateTextSize();
}
public function tabChange(from:String = "none", ind:int = 0, createOperation:Boolean = true):void {
if (from == "tabbar") {
tabSelectedIndex = tabBar.selectedIndex;
}
if (from == "sidelist") {
tabSelectedIndex = sideList.selectedIndex;
}
if (from == "operation") {
tabSelectedIndex = ind;
}
tabData[previousIndex].textData = textArea.text;
tabData[previousIndex].selectedActive = textArea.selectionActivePosition;
tabData[previousIndex].selectedAnchor = textArea.selectionAnchorPosition;
if (createOperation) {
var operation:TabOperation = new TabOperation(previousIndex, tabSelectedIndex, undoManager);
undoManager.pushUndo(operation);
}
previousIndex = tabSelectedIndex;
textArea.text = tabData[tabSelectedIndex].textData;
textArea.selectRange(tabData[tabSelectedIndex].selectedAnchor, tabData[tabSelectedIndex].selectedActive);
updateStatus();
countLines();
textChange();
}
private function tabContextClose(evt:ContextMenuEvent):void{
closeTab(rightclickTabIndex);
}
private function tabContextCloseOther(evt:ContextMenuEvent):void {
var len:int = tabData.length;
for (var i:int = 0; i < len; i++) {
if (i != rightclickTabIndex) {
closeTab(i);
}
}
}
private function tabRightClick(evt:MouseEvent):void {
var tabWidth:Number = tabBar.width / tabData.length;
var rcIndex:int = Math.floor(tabBar.mouseX / tabWidth);
rightclickTabIndex = rcIndex;
}
private function listRightClick(evt:MouseEvent):void {
var tabHeight:Number = 20;
var rcIndex:int = Math.floor((sideList.mouseY + sideList.scroller.verticalScrollBar.value) / tabHeight);
rightclickTabIndex = rcIndex;
}
private function onKeyDown(evt:KeyboardEvent):void{
if (evt.ctrlKey) {
// Ctrl+TAB - next tab
if (evt.keyCode == 9 && !evt.shiftKey) {
if (tabData.length - tabSelectedIndex > 1) {
tabSelectedIndex++;
tabChange();
}
}
// Ctrl+Shift+TAB - previous tab
if (evt.keyCode == 9 && evt.shiftKey) {
if (tabSelectedIndex > 0) {
tabSelectedIndex--;
tabChange();
}
}
// Ctrl+number (1-8) - go to numbered tab
if (evt.keyCode >= 49 && evt.keyCode <= 56) {
var num:int = evt.keyCode - 48;
if (tabData.length > num - 1) {
tabSelectedIndex = num - 1;
tabChange();
}
}
// Ctrl+9 - go to last tab
if (evt.keyCode == 57) {
tabSelectedIndex = tabData.length - 1;
tabChange();
}
}
}
private function closeSidePane():void{
pref_sidepane = !pref_sidepane
savePreferences();
updateTextSize();
}
private function countLines():void {
if (pref_linecount && !pref_wrap) {
var totalLines:int = textArea.text.split("
").length;
if (totalLines != lineDisplayedNum) {
updateTextSize();
updateLineCount(totalLines, totalLines-lineDisplayedNum, lineDisplayedNum);
lineDisplayedNum = totalLines;
}
}
}
private function updateLineCount(total:int, difference:int, current:int):void {
if (difference > 0) {
for (var i:int = current + 1; i < (total+1); i++) {
lineNumbers += "
" + (i);
}
}
if (difference < 0) {
var charsInTheEnd:int = 0;
for (var u:int = 0; u < -difference; u++) {
charsInTheEnd += ((current - u).toString().length + 1);
}
lineNumbers = lineCount.text.substring(0, lineCount.text.length - charsInTheEnd);
}
}
private function updateLineScroll():void{
lineCount.scroller.verticalScrollBar.value = textArea.scroller.verticalScrollBar.value;
}
private function doPrint():void {
var printJob:FlexPrintJob = new FlexPrintJob();
if (!printJob.start()) return;
tempText.visible = true;
tempText.setStyle("lineBreak", "toFit");
tempText.text = textArea.text;
tempText.width = printJob.pageWidth;
tempText.heightInLines = NaN;
tempText.setStyle("horizontalScrollPolicy", "off");
tempText.setStyle("verticalScrollPolicy", "off");
printJob.printAsBitmap = false;
printJob.addObject(tempText, "matchWidth");
printJob.send();
tempText.visible = false;
}
private function textChange():void{
canUndo = undoManager.canUndo();
canRedo = undoManager.canRedo();
focusManager.setFocus(textArea);
}
private function doUndo():void {
undoManager.undo();
textChange();
}
private function doRedo():void {
undoManager.redo();
textChange();
}
private function doOpen():void {
var file:File = new File();
file.browseForOpen("Open document", [new FileFilter("Text documents", "*.txt"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, fileLoad);
function fileLoad(evt:Event):void {
if(fileDuplicateCheck(file.nativePath)){
var stream:FileStream = new FileStream();
stream.open(file, FileMode.READ);
var str:String = stream.readUTFBytes(stream.bytesAvailable);
stream.close();
str = str.replace(File.lineEnding, "
");
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + file.name + " opened";
updateStatus();
tabData.addItem( { title:file.name, textData: str, saved:true, location:file.nativePath} );
tabSelectedIndex = tabData.length - 1;
tabChange();
updateTextSize();
}else {
Alert.show("File " + file.name + " is already open","Error");
}
}
}
private function fileDuplicateCheck(loc:String):Boolean {
var toReturn:Boolean = true;
for (var i:int = 0; i < tabData.length; i++) {
if (tabData[i].location == loc) {
toReturn = false;
tabSelectedIndex = i;
tabChange();
break;
}
}
return toReturn;
}
private function saveUpdate():void {
if (tabData[tabSelectedIndex].saved) {
tabData[tabSelectedIndex].saved = false;
tabBar.dataProvider = new ArrayCollection([]);
tabBar.dataProvider = tabData;
tabBar.selectedIndex = tabSelectedIndex;
sideList.dataProvider = tabData;
}
}
private function doSave(ind:int, updateIndexNeeded:Boolean = true):void {
if(!tabData[ind].saved){
if (tabData[ind].location != "") {
saveFile(textArea.text, tabData[ind].location, updateIndexNeeded);
statusMessage = "[ " + new Date().toLocaleTimeString() + " ] " + tabData[ind].title + " saved";
updateStatus();
tabData[ind].saved = true;
}else{
doSaveAs(textArea.text, ind, updateIndexNeeded);
}
}
}
private function doSaveAll():void {
for (var i:int = 0; i < tabData.length; i++) {
doSave(i);
}
}
private function refreshData():void {
tabBar.dataProvider = new ArrayCollection([]);
tabBar.dataProvider = tabData;
tabBar.selectedIndex = tabSelectedIndex;
sideList.dataProvider = tabData;
}
private function doSaveAs(text:String, ind:int, updateIndexNeeded:Boolean = true):void {
var file:File = new File();
file.browseForSave("Save file");
file.addEventListener(Event.SELECT, fileSave);
function fileSave(evt:Event):void {
if (file.name.length > 0) {
// See if user entered extension for the file (for example .txt)
// If not, add .txt by default
var extReg:RegExp = /.([a-z0-9]{2,})/i;
if (extReg.test(file.name)) {
saveFile(text, file.nativePath, updateIndexNeeded);
tabData[ind].location = file.nativePath;
tabData[ind].title = file.name;
tabData[ind].saved = true;
}else{
saveFile(text, file.nativePath + ".txt", updateIndexNeeded);
tabData[ind].location = file.nativePath + ".txt";
tabData[ind].title = file.name + ".txt";
tabData[ind].saved = true;
}
}else{
Alert.show("You need to enter a name for your file.", "Error");
}
}
}
private function saveFile(text:String, location:String, updateIndexNeeded:Boolean = true):void {
var file:File = new File(location);
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
var str:String = text;
str = str.replace(/
/g, File.lineEnding);
stream.writeUTFBytes(str);
stream.close();
if (closeAfterConfirm && tabsToClose == 0 && saveWait) {
FlexGlobals.topLevelApplication.close();
}
saveWait = false;
if (updateIndexNeeded) {
refreshData();
}
}
]]>
</fx:Script>
<fx:Declarations>
<fx:XML id="windowMenu">
<root>
<menuitem label="File">
<menuitem label="New" key="n" controlKey="true" />
<menuitem label="Open" key="o" controlKey="true" />
<menuitem label="Save" key="s" controlKey="true" />
<menuitem label="Save As" key="s" controlKey="true" shiftKey="true" />
<menuitem label="Save All" key="s" controlKey="true" altKey="true" />
<menuitem type="separator"/>
<menuitem label="Print" key="p" controlKey="true" />
</menuitem>
<menuitem label="Edit">
<menuitem label="Undo" key="z" controlKey="true" enabled="{canUndo}" />
<menuitem label="Redo" key="y" controlKey="true" enabled="{canRedo}" />
<menuitem type="separator"/>
<menuitem label="Cut" key="x" controlKey="true" />
<menuitem label="Copy" key="c" controlKey="true" />
<menuitem label="Paste" key="v" controlKey="true" />
<menuitem type="separator"/>
<menuitem label="Select all" key="a" controlKey="true" />
</menuitem>
<menuitem label="Settings">
<menuitem label="Word wrap" type="check" toggled="{pref_wrap}" />
<menuitem label="Font..."/>
</menuitem>
<menuitem label="View">
<menuitem label="Tool bar" type="check" toggled="{pref_toolbar}" />
<menuitem label="Status bar" type="check" toggled="{pref_status}" />
<menuitem label="Line count" type="check" toggled="{pref_linecount}" />
<menuitem label="Side pane" type="check" toggled="{pref_sidepane}" />
</menuitem>
</root>
</fx:XML>
<mx:ArrayCollection id="tabData">
<fx:Object title="Untitled" textData="" saved="false" seletedActive="0" selectedAnchor="0" location="" />
</mx:ArrayCollection>
<mx:ArrayCollection id="sidePaneData">
<fx:Object icon="@Embed(../lib/page.png)" tip="Tab management" />
<fx:Object icon="@Embed(../lib/folder_magnify.png)" tip="File browsing" />
<fx:Object icon="@Embed(../lib/book.png)" tip="Snippets" />
</mx:ArrayCollection>
<mx:ArrayCollection id="sidePaneTabHeadings">
<fx:String>Tab management</fx:String>
<fx:String>File browsing</fx:String>
<fx:String>Snippets</fx:String>
</mx:ArrayCollection>
</fx:Declarations>
<s:Group width="100%" height="100%">
<s:TextArea id="textArea" width="{textWidth}" height="{textHeight}" y="{textY}" x="{textX}" lineBreak="{(pref_wrap)?(toFit):(explicit)}" click="cursorFix(); updateStatus();" change="updateStatus(); countLines(); textChange(); saveUpdate();" keyDown="updateStatus();" borderVisible="false" focusThickness="0" />
<s:Scroller horizontalScrollPolicy="auto" verticalScrollPolicy="off" width="{tabWidth}" y="{tabY}">
<s:Group>
<custom:CustomTabBar id="tabBar" dataProvider="{tabData}" itemRenderer="CustomTab" height="22" tabClose="onTabClose(event);" change="tabChange(tabbar);" selectedIndex="{tabSelectedIndex}" labelField="saved">
<custom:layout>
<s:HorizontalLayout gap="-1" columnWidth="170" variableColumnWidth="false"/>
</custom:layout>
</custom:CustomTabBar>
</s:Group>
</s:Scroller>
<s:TextArea id="lineCount" width="{lineCountWidth}" text="{lineNumbers}" visible="{pref_linecount}" height="{textHeight}" y="{textY}" editable="false" selectable="false" mouseEnabled="false" textAlign="right" verticalScrollPolicy="off" horizontalScrollPolicy="off" />
<mx:HBox id="toolBar" width="100%" backgroundColor="#dddddd" height="30" visible="{pref_toolbar}" paddingTop="2" paddingLeft="3">
<custom:IconButton icon="@Embed(../lib/page.png)" toolTip="New document" click="doNew();" />
<custom:IconButton icon="@Embed(../lib/folder_page.png)" toolTip="Open" click="doOpen();" />
<custom:IconButton icon="@Embed(../lib/disk.png)" toolTip="Save" click="doSave(tabSelectedIndex);" />
<custom:IconButton icon="@Embed(../lib/disk_multiple.png)" toolTip="Save all" click="doSaveAll();" />
<custom:IconButton icon="@Embed(../lib/printer.png)" toolTip="Print" click="doPrint();" />
<s:Label text="|" fontSize="18" color="#bbbbbb" paddingTop="4" />
<custom:IconButton icon="@Embed(../lib/arrow_undo.png)" toolTip="Undo" enabled="{canUndo}" click="doUndo();" />
<custom:IconButton icon="@Embed(../lib/arrow_redo.png)" toolTip="Redo" enabled="{canRedo}" click="doRedo();" />
<s:Label text="|" fontSize="18" color="#bbbbbb" paddingTop="4" />
<custom:IconButton icon="@Embed(../lib/cut.png)" toolTip="Cut" click="doCut();" />
<custom:IconButton icon="@Embed(../lib/page_white_copy.png)" toolTip="Copy" click="doCopy();" />
<custom:IconButton icon="@Embed(../lib/paste_plain.png)" toolTip="Paste" click="doPaste();" />
</mx:HBox>
<mx:Box id="sidePane" width="{sidePaneWidth}" y="{sidePaneY}" x="{sidePaneX}" height="{sidePaneHeight}" backgroundColor="#dddddd" visible="{pref_sidepane}" paddingTop="5" paddingLeft="5" horizontalScrollPolicy="off">
<s:Group>
<s:Label text="{sidePaneTabHeadings.getItemAt(sidePaneButtons.selectedIndex)}" width="{sidePaneWidth}" />
<mx:Image source="@Embed(../lib/bullet_go.png)" top="-4" right="15" click="closeSidePane();" useHandCursor="true" buttonMode="true"/>
</s:Group>
<mx:ToggleButtonBar id="sidePaneButtons" dataProvider="{sidePaneData}" iconField="icon" width="{sidePaneWidth-10}" toolTipField="tip" />
<mx:ViewStack id="sidePaneStack" height="100%" selectedIndex="{sidePaneButtons.selectedIndex}">
<s:NavigatorContent id="tabs">
<custom:CustomList id="sideList" dataProvider="{tabData}" width="{sideContentWidth}" height="100%" itemRenderer="CustomListItem" selectedIndex="{tabSelectedIndex}" change="tabChange(sidelist);" tabClose="onListClose(event);" />
</s:NavigatorContent>
<s:NavigatorContent id="files">
<mx:FileSystemTree height="100%" width="100%" />
</s:NavigatorContent>
<s:NavigatorContent id="snippets">
<s:VGroup height="100%" width="100%">
<mx:Tree height="100%" width="100%" />
<s:Button width="100%" label="New snippet" />
<s:Button width="100%" label="Manage snippets" />
</s:VGroup>
</s:NavigatorContent>
</mx:ViewStack>
</mx:Box>
</s:Group>
<s:TextArea id="tempText" borderVisible="false" visible="false"/>
</s:WindowedApplication>
Everything works OK, except for the fact that if there are multiple unsaved tabs that have no location yet (Undefined ones) and you use Save All, the code throws an error. This is due to the fact that multiple browse dialog windows are called out at the same time.
We will work on this in the next part.
Thanks for reading!
Creating a Flex AIR Annotator app Part 21
In this part we begin working on the crop feature of our program.
Ive done cropping functionality before, specifically while making KirShot. I could just copy and paste the big code here, however, I prefer not to, for 3 reasons:
1) The code used is rather big and complex, it is integrated in KirShots particular environment, it would be very time-consuming and tiring to change all the values and references to reuse that code in KirAnnotator.
2) I am going to create a smaller and more reusable set of functions this time, that can be easily integrated in any program later.
3) We already have some usable functions in KirAnnotator - such as calculateRectValues(), which returns an array of rectangle values based on 4 coordinates.
First of all, go to the "stack" ViewStack container and add a new Group object called cropDraw, this will be used to draw the graphics on.
Declare these 4 variables:
In canvasOver(), set cursor to crossCursor if toolbarStacks index is 4:
In toolbarChange(), add a new else...if conditional which checks if selectedIndex of toolbarStack is 4. If so, set mouseEnabled of drawHitArea to true and size and coordinates of drawHitArea as shown:
Now, we can add the crop-related functions. There are 4 functions were adding today - crop_startDraw, crop_stopDraw, crop_moveDraw, and crop_updateDraw.
The first one receives mouse coordinates through parameters and applies their values to anchor variables, sets crop_drawing to true and sets values for crop_rectangle. Calls crop_updateDraw to draw the rectangle on stage.
The second function stops drawing by setting the boolean to false:
The third function checks if boolean is true, sets rectangle values with the help of our calculateRectValues() function and calls crop_updateDraw():
The final function draws the rectangle like this:
Now we need to call these functions somewhere.
In the beginning of drawDown():
In drawUp():
In drawMove():
Thats all for now.
Full code:
Thanks for reading!
Read more »
Ive done cropping functionality before, specifically while making KirShot. I could just copy and paste the big code here, however, I prefer not to, for 3 reasons:
1) The code used is rather big and complex, it is integrated in KirShots particular environment, it would be very time-consuming and tiring to change all the values and references to reuse that code in KirAnnotator.
2) I am going to create a smaller and more reusable set of functions this time, that can be easily integrated in any program later.
3) We already have some usable functions in KirAnnotator - such as calculateRectValues(), which returns an array of rectangle values based on 4 coordinates.
First of all, go to the "stack" ViewStack container and add a new Group object called cropDraw, this will be used to draw the graphics on.
<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;" />
</s:HGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<mx:Image id="imgHolder" />
<s:Group id="drawGroup">
<s:Group id="drawSprite" />
<s:Group id="tempSprite" />
</s:Group>
<mx:Box id="annHitArea" alpha="0" backgroundColor="#000000" click="openAnnotation(drawArea.mouseX, drawArea.mouseY);" />
<s:Group id="annotations">
</s:Group>
<s:Group id="cropDraw">
</s:Group>
<mx:Box id="drawHitArea" alpha="0" backgroundColor="#000000" mouseDown="drawDown();" mouseMove="drawMove();"/>
</s:NavigatorContent>
</mx:ViewStack>
Declare these 4 variables:
private var crop_rectangle:Array;
private var crop_anchorX:Number;
private var crop_anchorY:Number;
private var crop_drawing:Boolean = false;
In canvasOver(), set cursor to crossCursor if toolbarStacks index is 4:
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);
}
}
if (toolbarStack.selectedIndex == 4) cursorManager.setCursor(crossCursor, 2, -9, -10);
}
In toolbarChange(), add a new else...if conditional which checks if selectedIndex of toolbarStack is 4. If so, set mouseEnabled of drawHitArea to true and size and coordinates of drawHitArea as shown:
private function toolbarChange():void {
if (toolbarStack.selectedIndex == 2) {
updateLineExample();
drawHitArea.mouseEnabled = true;
}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;
}
}
Now, we can add the crop-related functions. There are 4 functions were adding today - crop_startDraw, crop_stopDraw, crop_moveDraw, and crop_updateDraw.
The first one receives mouse coordinates through parameters and applies their values to anchor variables, sets crop_drawing to true and sets values for crop_rectangle. Calls crop_updateDraw to draw the rectangle on stage.
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();
}
The second function stops drawing by setting the boolean to false:
private function crop_stopDraw():void {
crop_drawing = false;
}
The third function checks if boolean is true, sets rectangle values with the help of our calculateRectValues() function and calls crop_updateDraw():
private function crop_moveDraw(mouseX:Number, mouseY:Number):void {
if(crop_drawing){
crop_rectangle = calculateRectValues(mouseX, mouseY, crop_anchorX, crop_anchorY);
crop_updateDraw();
}
}
The final function draws the rectangle like this:
private function crop_updateDraw():void {
cropDraw.graphics.clear();
cropDraw.graphics.lineStyle(1, 0x000000);
cropDraw.graphics.drawRect(crop_rectangle[0], crop_rectangle[1], crop_rectangle[2], crop_rectangle[3]);
}
Now we need to call these functions somewhere.
In the beginning of drawDown():
// cropping
if(toolbarStack.selectedIndex == 4) crop_startDraw(drawArea.mouseX, drawArea.mouseY);
In drawUp():
// cropping
if(toolbarStack.selectedIndex == 4) crop_stopDraw();
In drawMove():
// cropping
if(toolbarStack.selectedIndex == 4) crop_moveDraw(drawArea.mouseX, drawArea.mouseY);
Thats all for now.
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="770" 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.Image;
import mx.controls.Label;
import mx.controls.TextArea;
import mx.core.FlexGlobals;
import mx.events.CloseEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
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/cursor_move.png")]
private var moveCursor: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 var currentlyDragging:*;
private var dragOffsetX:Number;
private var dragOffsetY:Number;
private var preferences:SharedObject = SharedObject.getLocal("kirannotatorPreferences");
private var selectedFormat:String;
private var selectedQuality:int;
private var crop_rectangle:Array;
private var crop_anchorX:Number;
private var crop_anchorY:Number;
private var crop_drawing: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.flush();
}
// Set preferences loaded from local storage
selectedFormat = preferences.data.selectedFormat;
selectedQuality = preferences.data.selectedQuality;
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);
}
}
if (toolbarStack.selectedIndex == 4) cursorManager.setCursor(crossCursor, 2, -9, -10);
}
private function canvasOut():void {
cursorManager.removeAllCursors();
}
private function drawDown():void {
// cropping
if(toolbarStack.selectedIndex == 4) crop_startDraw(drawArea.mouseX, drawArea.mouseY);
// drawing
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 {
// cropping
if(toolbarStack.selectedIndex == 4) crop_stopDraw();
// drawing
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 = "";
currentlyDragging = null;
if (toolbarStack.selectedIndex == 1) {
drawHitArea.mouseEnabled = false;
cursorManager.removeAllCursors();
updateDrawHitArea();
}
}
private function drawMove():void {
// cropping
if(toolbarStack.selectedIndex == 4) crop_moveDraw(drawArea.mouseX, drawArea.mouseY);
// drawing
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();
}
if (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;
}
}
}
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;
cropDraw.width = content.width;
cropDraw.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 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);
}
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;
annWidth.value = 200;
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.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.CLICK, annClick);
newAnn.addEventListener(MouseEvent.MOUSE_DOWN, annDown);
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.heightInLines = NaN;
editAnn.x = annX.value;
editAnn.y = annY.value;
}
private function deleteAnnotation():void {
annotations.removeElement(currentlyEditing);
}
private function annOver(evt:MouseEvent):void {
if (toolbarStack.selectedIndex == 3) {
evt.target.alpha = 0.5;
}
if (toolbarStack.selectedIndex == 1) {
cursorManager.setCursor(moveCursor, 2, -10, -10);
}
}
private function annOut(evt:MouseEvent):void{
evt.target.alpha = 1;
if (toolbarStack.selectedIndex == 1 && 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");
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 == 1) {
currentlyDragging = evt.currentTarget;
dragOffsetX = evt.currentTarget.mouseX;
dragOffsetY = evt.currentTarget.mouseY;
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 = 100;
}
private function closeNewfile():void {
PopUpManager.removePopUp(newfileWindow);
}
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();
}
bd = new BitmapData(content.width, content.height, false);
bd.draw(drawArea);
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_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_stopDraw():void {
crop_drawing = false;
}
private function crop_moveDraw(mouseX:Number, mouseY:Number):void {
if(crop_drawing){
crop_rectangle = calculateRectValues(mouseX, mouseY, crop_anchorX, crop_anchorY);
crop_updateDraw();
}
}
private function crop_updateDraw():void {
cropDraw.graphics.clear();
cropDraw.graphics.lineStyle(1, 0x000000);
cropDraw.graphics.drawRect(crop_rectangle[0], crop_rectangle[1], crop_rectangle[2], crop_rectangle[3]);
}
]]>
</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="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: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="340" 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" 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>
</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="newFile();" />
<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" click="saveFile();" />
<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 click the area to</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>
<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="400" height="200" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<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;" />
</s:HGroup>
</s:NavigatorContent>
<s:NavigatorContent>
<mx:Image id="imgHolder" />
<s:Group id="drawGroup">
<s:Group id="drawSprite" />
<s:Group id="tempSprite" />
</s:Group>
<mx:Box id="annHitArea" alpha="0" backgroundColor="#000000" click="openAnnotation(drawArea.mouseX, drawArea.mouseY);" />
<s:Group id="annotations">
</s:Group>
<s:Group id="cropDraw">
</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!
Subscribe to:
Posts (Atom)