<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DEcHRHY4cSp7ImA9WhVUFkw.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138</id><updated>2012-05-21T19:00:35.839+03:00</updated><category term="OOP in AS3" /><category term="blog related" /><category term="AS3 and XML" /><category term="SQL" /><category term="Making Kirpad step-by-step" /><category term="intermediate as3 tutorial" /><category term="Making KirSQLite step-by-step" /><category term="Making KirSizer step-by-step" /><category term="Physics" /><category term="Flex 4" /><category term="Adobe AIR" /><category term="beginner as3 tutorial" /><category term="AS3 and components" /><category term="Making Kirshot step-by-step" /><category term="Flash and HTML" /><category term="Making KirAnnotator step-by-step" /><title>ActionScript 3 for food</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://kirill-poletaev.blogspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>697</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/KirillPoletaev" /><feedburner:info uri="kirillpoletaev" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>KirillPoletaev</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry gd:etag="W/&quot;CUEFQXg_eSp7ImA9WhVUFUU.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-1298783762416294516</id><published>2012-05-21T10:00:00.000+03:00</published><updated>2012-05-21T10:00:10.641+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-21T10:00:10.641+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 20</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Y5dR9-WfrIb8vLzEbOSiI1jVB8Y/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Y5dR9-WfrIb8vLzEbOSiI1jVB8Y/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Y5dR9-WfrIb8vLzEbOSiI1jVB8Y/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Y5dR9-WfrIb8vLzEbOSiI1jVB8Y/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will improve our "Add column" function.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Last time we created a function that uses the "ALTER TABLE" SQL command to create a new table. This, however, is not the perfect solution, since there are numerous disadvantages in using this method. When we use "ALTER TABLE", we cannot use the "PRIMARY KEY", "AUTOINCREMENT" or "UNIQUE" clauses - their respective items in the form become useless.&lt;br /&gt;
&lt;br /&gt;
There is a way to bypass this. We can rewrite the whole table from scratch, and just add this column next to the other ones when creating the table. Of course, before rewriting the table, we'll have to store its values in a temporary backup table, then delete the existing one and only then create a new table with new columns, followed by adding all the values from the backup database. Even then we have to watch out for errors - if something goes wrong, we might lose the whole table. So we'll need to keep this backup table until the table is completed, and if there is an error - rename the backup table to the initial table name to restore all the data.&lt;br /&gt;
&lt;br /&gt;
This is pretty hacky, but it's the only way to do it right. There's also no other way to delete or edit columns, so we'll be using this method again in the future. For now, let's get the "Add column" function working properly.&lt;br /&gt;
&lt;br /&gt;
Find the addColumn() function. We'll now heavily edit this function.&lt;br /&gt;
&lt;br /&gt;
First of all, set the emphasized property of the "Update selected" button to false. Then declare a variable that stores the name of the current table:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;col_b_update.emphasized = false;
var prevTableName:String = tableTree.selectedItem.@label;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then goes the if... statement. Here, we'll add a few lines to load the schema in order to get the string of all columns in the table along with their parameters (just like we did in columnSelect()):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (col_name.text != "" &amp;&amp; col_data.textInput.text != "" &amp;&amp; (col_null.selected || col_default.text != "")) {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then we create the sqlText variable, but this time we use CREATE TABLE instead of ALTER TABLE. The code following that line remains unchanged:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var sqlText:String = "CREATE TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label + " (";
sqlText += fullSQL + ", ";
// add the new column
sqlText += col_name.text + " " + col_data.textInput.text + " ";
if (col_key.selected) sqlText += "PRIMARY KEY ";
if (col_key.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_key.selected &amp;&amp; col_auto.selected) sqlText += "AUTOINCREMENT ";
if (!col_null.selected) sqlText += "NOT NULL ";
if (!col_null.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_unique.selected) sqlText += "UNIQUE ";
if (col_unique.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_default.text != "") {
sqlText += "DEFAULT ";
if (isNaN(Number(col_default.text))) sqlText += '"' + col_default.text + '"';
if (!isNaN(Number(col_default.text))) sqlText += col_default.text;
}
sqlText += ");";
lastStatement(sqlText);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
After that we need to create a backup table. In order to do that, we need to know what name the backup table should have. It will be created in the same database as the initial table, but we must make sure it has a unique name. We set the name to "backup" and then check if there already is a table like that. If there is, add "0" to the end of the table name and repeat.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// Create backup
var backupName:String = "backup";
while (!tableIsUnique(backupName)) {
backupName += "0";
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
I created this function outside of addColumn() to be able to check if a table is unique:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableIsUnique(name:String):Boolean {
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData..tb.length(); i++) {
if (dbData..tb[i].@label == name) {
r = false;
break;
}
}
return r;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, after we know the name of the backup table, we create it using CREATE TABLE. Use the fullSQL variable's value to create the same columns as in the initial table:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var bstat:SQLStatement = new SQLStatement();
bstat.sqlConnection = connection;
bstat.text = "CREATE TABLE " + selectedDatabase + "." + backupName + " (" + fullSQL + ");";
bstat.execute();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then we copy all the existing data from the table to the backup table using an INSERT INTO SQL command:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// Copy data to backup
var cstat:SQLStatement = new SQLStatement();
cstat.sqlConnection = connection;
cstat.text = "INSERT INTO " + selectedDatabase + "." + backupName + " SELECT * FROM " + selectedDatabase + "." + tableTree.selectedItem.@label;
cstat.execute();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we can remove the initial table:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// Delete initial table
var dstat:SQLStatement = new SQLStatement();
dstat.sqlConnection = connection;
dstat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
dstat.execute();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
And finally execute the statement with sqlText query - the one that creates a new table with the new columns:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// Create new table
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = sqlText;
stat.execute( -1, new Responder(newColumnSuccess, newColumnError));
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In newColumnSuccess(), we insert the values from backup database into the new database, and then delete the backup.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function newColumnSuccess(evt:SQLResult):void {
// Insert previous values
var istat:SQLStatement = new SQLStatement();
istat.sqlConnection = connection;
istat.text = "INSERT INTO " + selectedDatabase + "." + prevTableName + " (" + columnNames() + ") SELECT " + columnNames() + " FROM " + selectedDatabase + "." + backupName;
istat.execute();
// Delete backup
var bdstat:SQLStatement = new SQLStatement();
bdstat.sqlConnection = connection;
bdstat.text = "DROP TABLE " + selectedDatabase + "." + backupName;
bdstat.execute();
tableSelect();
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
You can see I used a method called columnNames() to list all the columns. It is a simple function that returns a string consisting of column names separated with a comma:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function columnNames():String {
var r:String = "";
for (var i:int = 0; i &amp;lt; columnData.length; i++) {
r += columnData[i].name;
if (i &amp;lt; columnData.length - 1) r += ", ";
}
return r;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the newColumnError() function, we warn the user of the error, and then restore the initial table by renaming the backup table to the initial name. We can rename a table using ALTER TABLE command:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function newColumnError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details + "\n\nRestoring the database using backup...", "Error");
// Restore table
var rstat:SQLStatement = new SQLStatement();
rstat.sqlConnection = connection;
rstat.text = "ALTER TABLE " + selectedDatabase + "." + backupName + " RENAME TO " + prevTableName
rstat.execute();
tableSelect();
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Phew! We're done. Full function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function addColumn():void {
col_b_update.emphasized = false;
var prevTableName:String = tableTree.selectedItem.@label;
if (col_name.text != "" &amp;&amp; col_data.textInput.text != "" &amp;&amp; (col_null.selected || col_default.text != "")) {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
var sqlText:String = "CREATE TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label + " (";
sqlText += fullSQL + ", ";
// add the new column
sqlText += col_name.text + " " + col_data.textInput.text + " ";
if (col_key.selected) sqlText += "PRIMARY KEY ";
if (col_key.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_key.selected &amp;&amp; col_auto.selected) sqlText += "AUTOINCREMENT ";
if (!col_null.selected) sqlText += "NOT NULL ";
if (!col_null.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_unique.selected) sqlText += "UNIQUE ";
if (col_unique.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_default.text != "") {
sqlText += "DEFAULT ";
if (isNaN(Number(col_default.text))) sqlText += '"' + col_default.text + '"';
if (!isNaN(Number(col_default.text))) sqlText += col_default.text;
}
sqlText += ");";
lastStatement(sqlText);

// Create backup
var backupName:String = "backup";
while (!tableIsUnique(backupName)) {
backupName += "0";
}
var bstat:SQLStatement = new SQLStatement();
bstat.sqlConnection = connection;
bstat.text = "CREATE TABLE " + selectedDatabase + "." + backupName + " (" + fullSQL + ");";
bstat.execute();

// Copy data to backup
var cstat:SQLStatement = new SQLStatement();
cstat.sqlConnection = connection;
cstat.text = "INSERT INTO " + selectedDatabase + "." + backupName + " SELECT * FROM " + selectedDatabase + "." + tableTree.selectedItem.@label;
cstat.execute();

// Delete initial table
var dstat:SQLStatement = new SQLStatement();
dstat.sqlConnection = connection;
dstat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
dstat.execute();

// Create new table
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = sqlText;
stat.execute( -1, new Responder(newColumnSuccess, newColumnError));
}else {
Alert.show("Please fill all the required fields!", "Error");
}
function newColumnSuccess(evt:SQLResult):void {
// Insert previous values
var istat:SQLStatement = new SQLStatement();
istat.sqlConnection = connection;
istat.text = "INSERT INTO " + selectedDatabase + "." + prevTableName + " (" + columnNames() + ") SELECT " + columnNames() + " FROM " + selectedDatabase + "." + backupName;
istat.execute();
// Delete backup
var bdstat:SQLStatement = new SQLStatement();
bdstat.sqlConnection = connection;
bdstat.text = "DROP TABLE " + selectedDatabase + "." + backupName;
bdstat.execute();
tableSelect();
}
function newColumnError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details + "\n\nRestoring the database using backup...", "Error");
// Restore table
var rstat:SQLStatement = new SQLStatement();
rstat.sqlConnection = connection;
rstat.text = "ALTER TABLE " + selectedDatabase + "." + backupName + " RENAME TO " + prevTableName
rstat.execute();
tableSelect();
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="columnData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="conflictTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;---&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ABORT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;FAIL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;IGNORE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ROLLBACK&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REPLACE&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="dataTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;NONE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;INTEGER&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;TEXT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REAL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;NUMERIC&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{ 
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if(col_name!=null){
col_name.text = "";
col_data.selectedIndex = 0;
col_key.selected = false;
col_auto.selected = false;
col_unique.selected = false;
col_null.selected = false;
col_default.text = "";
col_conflict.selectedIndex = 0;
}
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
lastStatement(stat.text);
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
lastStatement(stat.text);
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
lastStatement(stat.text);
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function columnSelect():void {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;

// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;

// enable or disable ON CONFLICT
checkConflict();
// unhighlight "Update selected"
col_b_update.emphasized = false;
}

private function checkConflict():void {
if (col_key.selected || !col_null.selected || col_unique.selected) {
col_conflict.enabled = true;
}else {
col_conflict.enabled = false;
}
}

private function formChange():void {
checkConflict();
if (columnList.selectedItems.length &amp;gt; 0) {
col_b_update.emphasized = true;
}
}

private function addColumn():void {
col_b_update.emphasized = false;
var prevTableName:String = tableTree.selectedItem.@label;
if (col_name.text != "" &amp;&amp; col_data.textInput.text != "" &amp;&amp; (col_null.selected || col_default.text != "")) {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
var sqlText:String = "CREATE TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label + " (";
sqlText += fullSQL + ", ";
// add the new column
sqlText += col_name.text + " " + col_data.textInput.text + " ";
if (col_key.selected) sqlText += "PRIMARY KEY ";
if (col_key.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_key.selected &amp;&amp; col_auto.selected) sqlText += "AUTOINCREMENT ";
if (!col_null.selected) sqlText += "NOT NULL ";
if (!col_null.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_unique.selected) sqlText += "UNIQUE ";
if (col_unique.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_default.text != "") {
sqlText += "DEFAULT ";
if (isNaN(Number(col_default.text))) sqlText += '"' + col_default.text + '"';
if (!isNaN(Number(col_default.text))) sqlText += col_default.text;
}
sqlText += ");";
lastStatement(sqlText);

// Create backup
var backupName:String = "backup";
while (!tableIsUnique(backupName)) {
backupName += "0";
}
var bstat:SQLStatement = new SQLStatement();
bstat.sqlConnection = connection;
bstat.text = "CREATE TABLE " + selectedDatabase + "." + backupName + " (" + fullSQL + ");";
bstat.execute();

// Copy data to backup
var cstat:SQLStatement = new SQLStatement();
cstat.sqlConnection = connection;
cstat.text = "INSERT INTO " + selectedDatabase + "." + backupName + " SELECT * FROM " + selectedDatabase + "." + tableTree.selectedItem.@label;
cstat.execute();

// Delete initial table
var dstat:SQLStatement = new SQLStatement();
dstat.sqlConnection = connection;
dstat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
dstat.execute();

// Create new table
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = sqlText;
stat.execute( -1, new Responder(newColumnSuccess, newColumnError));
}else {
Alert.show("Please fill all the required fields!", "Error");
}
function newColumnSuccess(evt:SQLResult):void {
// Insert previous values
var istat:SQLStatement = new SQLStatement();
istat.sqlConnection = connection;
istat.text = "INSERT INTO " + selectedDatabase + "." + prevTableName + " (" + columnNames() + ") SELECT " + columnNames() + " FROM " + selectedDatabase + "." + backupName;
istat.execute();
// Delete backup
var bdstat:SQLStatement = new SQLStatement();
bdstat.sqlConnection = connection;
bdstat.text = "DROP TABLE " + selectedDatabase + "." + backupName;
bdstat.execute();
tableSelect();
}
function newColumnError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details + "\n\nRestoring the database using backup...", "Error");
// Restore table
var rstat:SQLStatement = new SQLStatement();
rstat.sqlConnection = connection;
rstat.text = "ALTER TABLE " + selectedDatabase + "." + backupName + " RENAME TO " + prevTableName
rstat.execute();
tableSelect();
}
}

private function tableIsUnique(name:String):Boolean {
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData..tb.length(); i++) {
if (dbData..tb[i].@label == name) {
r = false;
break;
}
}
return r;
}

private function columnNames():String {
var r:String = "";
for (var i:int = 0; i &amp;lt; columnData.length; i++) {
r += columnData[i].name;
if (i &amp;lt; columnData.length - 1) r += ", ";
}
return r;
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" change="columnSelect();" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="col_b_add" label="Add column" enabled="{isTableSelected}" click="addColumn();" /&amp;gt;
&amp;lt;s:Button id="col_b_update" label="Update selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;s:Button id="col_b_delete" label="Delete selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name" required="true"&amp;gt;
&amp;lt;s:TextInput id="col_name" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type" required="true"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto" change="formChange();" enabled="{col_key.selected}" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null" change="formChange();" selected="true" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value" required="{!col_null.selected}"&amp;gt;
&amp;lt;s:TextArea id="col_default" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-1298783762416294516?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/pCzpWI-XlxI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/1298783762416294516/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_21.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/1298783762416294516?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/1298783762416294516?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/pCzpWI-XlxI/kirsqlite-flex-air-database-manager_21.html" title="KirSQLite - Flex AIR Database Manager: Part 20" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_21.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUEQ3szfCp7ImA9WhVUFEQ.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-6357025830646200622</id><published>2012-05-20T10:00:00.000+03:00</published><updated>2012-05-20T10:00:02.584+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-20T10:00:02.584+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 19</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/QTJS3H6lCIOh0kjmXmWOktPlUg4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QTJS3H6lCIOh0kjmXmWOktPlUg4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/QTJS3H6lCIOh0kjmXmWOktPlUg4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QTJS3H6lCIOh0kjmXmWOktPlUg4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll add the ability to add new columns to a table.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
First of all, we'll go to the Form container and do a few changes there.&lt;br /&gt;
&lt;br /&gt;
Set the Name and Data type fields required by setting their "required" property to true. Set Default Value field's required property bound to the opposite of col_null's selected property. This will make the default field required only if the user has disallowed NULL values.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name" required="true"&amp;gt;
&amp;lt;s:TextInput id="col_name" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type" required="true"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto" change="formChange();" enabled="{col_key.selected}" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null" change="formChange();" selected="true" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value" required="{!col_null.selected}"&amp;gt;
&amp;lt;s:TextArea id="col_default" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now find the "Add column" Button. Set its click event handler to addColumn():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:Button id="col_b_add" label="Add column" enabled="{isTableSelected}" click="addColumn();" /&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the addColumn() function add a conditional that checks if col_name's text field is not blank, col_data's textInput is not blank and if col_null is selected or col_default is not blank. We're basically checking if all the required fields are filled.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (col_name.text != "" &amp;&amp; col_data.textInput.text != "" &amp;&amp; (col_null.selected || col_default.text!="")) {

}else {
Alert.show("Please fill all the required fields!", "Error");
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the conditional, create a variable sqlText. We'll use it to compose an SQL query needed to add a column. To add a column, we can use the ALTER TABLE command:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var sqlText:String = "ALTER TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label + " ADD COLUMN ";
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Add the name of the new column and its data type:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;sqlText += col_name.text + " " + col_data.textInput.text + " ";
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Check if col_key is selected. Add PRIMARY KEY if so. Check if col_key is selected and something is selected in the ON CONFLICT ComboBox, add ON CONFLICT with the needed response to the query:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (col_key.selected) sqlText += "PRIMARY KEY ";
if (col_key.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Add AUTOINCREMENT if needed:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (col_auto.selected) sqlText += "AUTOINCREMENT ";
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Handle col_null and col_unique similarly:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (!col_null.selected) sqlText += "NOT NULL ";
if (!col_null.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_unique.selected) sqlText += "UNIQUE ";
if (col_unique.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we'll check if the default field is not empty. If it isn't, check if the text in the field is a number. If it's a number, don't use quotes when inputting the values.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (col_default.text != "") {
sqlText += "DEFAULT ";
if (isNaN(Number(col_default.text))) sqlText += '"' + col_default.text + '"';
if (!isNaN(Number(col_default.text))) sqlText += col_default.text;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Use lastStatement() function to add the query to the history. Then create an SQLStatement object with this query as text, execute it and remember to add a Responder object.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;lastStatement(sqlText);
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = sqlText;
stat.execute( -1, new Responder(newColumnSuccess, newColumnError));
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Handle success and errors like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function newColumnSuccess(evt:SQLResult):void {
tableSelect();
}
function newColumnError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error")
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function addColumn():void {
if (col_name.text != "" &amp;&amp; col_data.textInput.text != "" &amp;&amp; (col_null.selected || col_default.text!="")) {
var sqlText:String = "ALTER TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label + " ADD COLUMN ";
sqlText += col_name.text + " " + col_data.textInput.text + " ";
if (col_key.selected) sqlText += "PRIMARY KEY ";
if (col_key.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_auto.selected) sqlText += "AUTOINCREMENT ";
if (!col_null.selected) sqlText += "NOT NULL ";
if (!col_null.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_unique.selected) sqlText += "UNIQUE ";
if (col_unique.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_default.text != "") {
sqlText += "DEFAULT ";
if (isNaN(Number(col_default.text))) sqlText += '"' + col_default.text + '"';
if (!isNaN(Number(col_default.text))) sqlText += col_default.text;
}
lastStatement(sqlText);
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = sqlText;
stat.execute( -1, new Responder(newColumnSuccess, newColumnError));
}else {
Alert.show("Please fill all the required fields!", "Error");
}
function newColumnSuccess(evt:SQLResult):void {
tableSelect();
}
function newColumnError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error")
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code below.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="columnData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="conflictTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;---&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ABORT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;FAIL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;IGNORE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ROLLBACK&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REPLACE&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="dataTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;NONE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;INTEGER&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;TEXT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REAL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;NUMERIC&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{ 
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if(col_name!=null){
col_name.text = "";
col_data.selectedIndex = 0;
col_key.selected = false;
col_auto.selected = false;
col_unique.selected = false;
col_null.selected = false;
col_default.text = "";
col_conflict.selectedIndex = 0;
}
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
lastStatement(stat.text);
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
lastStatement(stat.text);
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
lastStatement(stat.text);
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function columnSelect():void {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;

// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;

// enable or disable ON CONFLICT
checkConflict();
// unhighlight "Update selected"
col_b_update.emphasized = false;
}

private function checkConflict():void {
if (col_key.selected || !col_null.selected || col_unique.selected) {
col_conflict.enabled = true;
}else {
col_conflict.enabled = false;
}
}

private function formChange():void {
checkConflict();
if (columnList.selectedItems.length &amp;gt; 0) {
col_b_update.emphasized = true;
}
}

private function addColumn():void {
if (col_name.text != "" &amp;&amp; col_data.textInput.text != "" &amp;&amp; (col_null.selected || col_default.text!="")) {
var sqlText:String = "ALTER TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label + " ADD COLUMN ";
sqlText += col_name.text + " " + col_data.textInput.text + " ";
if (col_key.selected) sqlText += "PRIMARY KEY ";
if (col_key.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_auto.selected) sqlText += "AUTOINCREMENT ";
if (!col_null.selected) sqlText += "NOT NULL ";
if (!col_null.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_unique.selected) sqlText += "UNIQUE ";
if (col_unique.selected &amp;&amp; col_conflict.selectedIndex &amp;gt; 0) sqlText += "ON CONFLICT " + col_conflict.selectedLabel + " ";
if (col_default.text != "") {
sqlText += "DEFAULT ";
if (isNaN(Number(col_default.text))) sqlText += '"' + col_default.text + '"';
if (!isNaN(Number(col_default.text))) sqlText += col_default.text;
}
lastStatement(sqlText);
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = sqlText;
stat.execute( -1, new Responder(newColumnSuccess, newColumnError));
}else {
Alert.show("Please fill all the required fields!", "Error");
}
function newColumnSuccess(evt:SQLResult):void {
tableSelect();
}
function newColumnError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error")
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" change="columnSelect();" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="col_b_add" label="Add column" enabled="{isTableSelected}" click="addColumn();" /&amp;gt;
&amp;lt;s:Button id="col_b_update" label="Update selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;s:Button id="col_b_delete" label="Delete selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name" required="true"&amp;gt;
&amp;lt;s:TextInput id="col_name" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type" required="true"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto" change="formChange();" enabled="{col_key.selected}" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null" change="formChange();" selected="true" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value" required="{!col_null.selected}"&amp;gt;
&amp;lt;s:TextArea id="col_default" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-6357025830646200622?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/kgbSxwZ6Plg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/6357025830646200622/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_20.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6357025830646200622?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6357025830646200622?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/kgbSxwZ6Plg/kirsqlite-flex-air-database-manager_20.html" title="KirSQLite - Flex AIR Database Manager: Part 19" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_20.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8ERno_cSp7ImA9WhVUFEw.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-2824143342464349327</id><published>2012-05-19T10:00:00.000+03:00</published><updated>2012-05-19T10:00:07.449+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-19T10:00:07.449+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 18</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/jXjZifIAGTasJsvwblx5bIoh0NU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jXjZifIAGTasJsvwblx5bIoh0NU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/jXjZifIAGTasJsvwblx5bIoh0NU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jXjZifIAGTasJsvwblx5bIoh0NU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will add a logical connection between the components we currently have on the "Edit columns" page.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
The connections I'm talking about are supposed to enable, disable and highlight certain components, based on the values of others.&lt;br /&gt;
&lt;br /&gt;
Firstly, find the 3 buttons - "Add column", "Update selected" and "Delete selected". Set their ids as shown below, set the first button's enabled property bound to isTableSelected and also set the last two buttons' enabled properties bound to columnList's length of selected items. If there are any items selected - make those buttons enabled, disable them otherwise.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="col_b_add" label="Add column" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;s:Button id="col_b_update" label="Update selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;s:Button id="col_b_delete" label="Delete selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Set the "change" event handlers of all the components inside the form to formChange(). Set the AutoIncrement CheckBox's enabled property bound to col_key's selected value.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name"&amp;gt;
&amp;lt;s:TextInput id="col_name" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto" change="formChange();" enabled="{col_key.selected}" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value"&amp;gt;
&amp;lt;s:TextArea id="col_default" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the formChange() function, we'll do two things. Firstly, we'll set the "ON CONFLICT" ComboBox enabled or disabled, based on certain values. Then we'll check if an item is currently selected in the columnList, and if so - emphasize the "Update selected" button. We'll have all the "ON CONFLICT" related code written in a function called checkConflict(), so just call that for now:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function formChange():void {
checkConflict();
if (columnList.selectedItems.length &amp;gt; 0) {
col_b_update.emphasized = true;
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The checkConflict() function checks if any of the three conditionals are true: if the current column is a primary key one, if null is not allowed, and if the column is unique. If any of these are true, enable the conflict ComboBox, otherwise - disable. The conditions are such in SQLite, the ON CONFLICT clause may only be used in situations like the ones I stated.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function checkConflict():void {
if (col_key.selected || !col_null.selected || col_unique.selected) {
col_conflict.enabled = true;
}else {
col_conflict.enabled = false;
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Go to columnSelect() and in the end of the function call checkConflict() and unemphasize "Update selected" button:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function columnSelect():void {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;

// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;

// enable or disable ON CONFLICT
checkConflict();
// unhighlight "Update selected"
col_b_update.emphasized = false;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Go to tableSelect() and in the beginning of the function add an if statement, that checks if col_name object exists (doesn't equal null). If so, reset the values of all form items:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if(col_name!=null){
col_name.text = "";
col_data.selectedIndex = 0;
col_key.selected = false;
col_auto.selected = false;
col_unique.selected = false;
col_null.selected = false;
col_default.text = "";
col_conflict.selectedIndex = 0;
}
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="columnData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="conflictTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;---&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ABORT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;FAIL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;IGNORE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ROLLBACK&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REPLACE&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="dataTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;NONE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;INTEGER&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;TEXT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REAL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;NUMERIC&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if(col_name!=null){
col_name.text = "";
col_data.selectedIndex = 0;
col_key.selected = false;
col_auto.selected = false;
col_unique.selected = false;
col_null.selected = false;
col_default.text = "";
col_conflict.selectedIndex = 0;
}
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function columnSelect():void {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;

// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;

// enable or disable ON CONFLICT
checkConflict();
// unhighlight "Update selected"
col_b_update.emphasized = false;
}

private function checkConflict():void {
if (col_key.selected || !col_null.selected || col_unique.selected) {
col_conflict.enabled = true;
}else {
col_conflict.enabled = false;
}
}

private function formChange():void {
checkConflict();
if (columnList.selectedItems.length &amp;gt; 0) {
col_b_update.emphasized = true;
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" change="columnSelect();" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="col_b_add" label="Add column" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;s:Button id="col_b_update" label="Update selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;s:Button id="col_b_delete" label="Delete selected" enabled="{columnList.selectedItems.length &amp;gt; 0}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name"&amp;gt;
&amp;lt;s:TextInput id="col_name" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto" change="formChange();" enabled="{col_key.selected}" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null" change="formChange();" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value"&amp;gt;
&amp;lt;s:TextArea id="col_default" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" change="formChange();"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-2824143342464349327?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/ha8_IWpDKlg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/2824143342464349327/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_19.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2824143342464349327?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2824143342464349327?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/ha8_IWpDKlg/kirsqlite-flex-air-database-manager_19.html" title="KirSQLite - Flex AIR Database Manager: Part 18" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_19.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMESHg6cCp7ImA9WhVUE08.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-779914898687949694</id><published>2012-05-18T10:00:00.000+03:00</published><updated>2012-05-18T10:00:09.618+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-18T10:00:09.618+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 17</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/QOgyFVzGyopPKR43-i9mSamDnww/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QOgyFVzGyopPKR43-i9mSamDnww/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/QOgyFVzGyopPKR43-i9mSamDnww/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QOgyFVzGyopPKR43-i9mSamDnww/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will add the ability to view details of each column in the table.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
When the user selects a column, its details will be automatically displayed in the form next to the list. We've created this form in the last tutorial, but we haven't given ids to the objects. Let's do that right now. Set ids of each object inside the FormItem containers, and also set the dataProviders of the DataType and OnConflict ComboBoxes to dataTypes and conflictTypes. Bind the enabled property of the Form container to isTableSelected:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name"&amp;gt;
&amp;lt;s:TextInput id="col_name"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value"&amp;gt;
&amp;lt;s:TextArea id="col_default"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Let's declare these ArrayCollections now. The conflictTypes one is either blank ("---"), or one of the 5 possible conflict types in SQLite:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:ArrayCollection id="conflictTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;---&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ABORT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;FAIL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;IGNORE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ROLLBACK&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REPLACE&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Set items of dataTypes to 5 of the most used ones, however, the user will not be limited to just these data types. They will be able to enter their own value.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:ArrayCollection id="dataTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;NONE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;INTEGER&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;TEXT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REAL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;NUMERIC&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now find the List component and set its change event handler to columnSelect().&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" change="columnSelect();" /&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This function is where all the action happens.&lt;br /&gt;
&lt;br /&gt;
First of all, we load the table schema to read the table's "sql" property, since it is the query that we'll be parsing and extracting data from.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We need to get rid of the unneeded text in the query and just leave columns separated by commas.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Get the current data related to the currentColumn using the selectedIndex of columnList:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Delete the name of the column from this text, because it will only get in our way:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, we will find and extract the DEFAULT value from the query. Create a regular expression pattern that matches "DEFAULT &amp;lt;value&amp;gt;". Keep in mind that the value can be a number (for example, 3) or a text piece in quotes (for example, "hello"). Find the match using regex, delete the "DEFAULT &amp;lt;value&amp;gt;" piece from currentParameters and then get rid of the word "DEFAULT" and the space after it from the match. If there are any quotes in the match, get rid of the first and last quotes:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we will look for an ON CONFLICT match. Create a regular expression also, extract it, delete from currentParameters and get rid of "ON CONFLICT" with a space character from the match:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we apply the values we've extracted to the form. We can check primary key, autoincrement, unique and not null values by simply using an indexOf() method on currentParameters and then setting the selected properties of the respective CheckBoxes to true or false, depending on the results. Set default text to defaultMatch value, and then do a few if... statements to set the selectedIndex of the conflict ComboBox:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We can read the data type without having to extract it from the SQL - just by reading the schema:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function columnSelect():void {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;

// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="columnData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="conflictTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;---&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ABORT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;FAIL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;IGNORE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;ROLLBACK&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REPLACE&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="dataTypes"&amp;gt;
&amp;lt;fx:String&amp;gt;NONE&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;INTEGER&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;TEXT&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;REAL&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;NUMERIC&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function columnSelect():void {
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
var fullSQL:String = schema.tables[0].sql;
// extract the text inside the ( )
fullSQL = fullSQL.substring( fullSQL.indexOf("(") + 1 , fullSQL.lastIndexOf(")") );
// split all columns into an array
var columns:Array = fullSQL.split(",");
// get the currently selected column
var currentColumn:String = columns[columnList.selectedIndex];
// delete the name of the column from this text
var currentParameters:String = currentColumn.substr(currentColumn.indexOf(columnList.selectedItem.name) + columnList.selectedItem.name.length + 1);
// find DEFAULT and extract it
var defaultMatch:String = "";
var defaultPattern:RegExp = /((DEFAULT)\s((".+")|([0-9]+)))/i;
if (currentParameters.match(defaultPattern)) {
defaultMatch = currentParameters.match(defaultPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(defaultMatch, "");
// delete "DEFAULT" from the match
defaultMatch = defaultMatch.substr(8);
// if any quotes are found, remove the first and last symbols
if (defaultMatch.indexOf('"') != -1) {
defaultMatch = defaultMatch.substring(1, defaultMatch.length - 1);
}
}
// find ON CONFLICT and extract it
var conflictMatch:String = "";
var conflictPattern:RegExp = /((ON CONFLICT)\s(ABORT|FAIL|IGNORE|ROLLBACK|REPLACE))/i;
if (currentParameters.match(conflictPattern)) {
conflictMatch = currentParameters.match(conflictPattern)[0];
// delete it from currentParameters
currentParameters = currentParameters.replace(conflictMatch, "");
// delete "ON CONFLICT" from the match
conflictMatch = conflictMatch.substr(12);
}
// apply values
col_name.text = columnList.selectedItem.name;
col_key.selected = (currentParameters.toUpperCase().indexOf("PRIMARY KEY") != -1)?(true):(false);
col_auto.selected = (currentParameters.toUpperCase().indexOf("AUTOINCREMENT") != -1)?(true):(false);
col_unique.selected = (currentParameters.toUpperCase().lastIndexOf("UNIQUE") != -1)?(true):(false);
col_null.selected = (currentParameters.toUpperCase().indexOf("NOT NULL") != -1)?(false):(true);
col_default.text = defaultMatch;
col_conflict.selectedIndex = 0;
if (conflictMatch.toUpperCase() == "ABORT") col_conflict.selectedIndex = 1;
if (conflictMatch.toUpperCase() == "FAIL") col_conflict.selectedIndex = 2;
if (conflictMatch.toUpperCase() == "IGNORE") col_conflict.selectedIndex = 3;
if (conflictMatch.toUpperCase() == "ROLLBACK") col_conflict.selectedIndex = 4;
if (conflictMatch.toUpperCase() == "REPLACE") col_conflict.selectedIndex = 5;

// read data type
col_data.textInput.text = schema.tables[0].columns[columnList.selectedIndex].dataType;
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" change="columnSelect();" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="Add column" /&amp;gt;
&amp;lt;s:Button label="Update selected" /&amp;gt;
&amp;lt;s:Button label="Delete selected" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Form enabled="{isTableSelected}"&amp;gt;
&amp;lt;mx:FormItem label="Name"&amp;gt;
&amp;lt;s:TextInput id="col_name"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type"&amp;gt;
&amp;lt;s:ComboBox id="col_data" dataProvider="{dataTypes}"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox id="col_key"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="AutoIncrement"&amp;gt;
&amp;lt;s:CheckBox id="col_auto"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox id="col_unique"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox id="col_null"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value"&amp;gt;
&amp;lt;s:TextArea id="col_default"/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;mx:ComboBox id="col_conflict" dataProvider="{conflictTypes}" editable="false" /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-779914898687949694?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/Sc9rPOGdQpc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/779914898687949694/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_18.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/779914898687949694?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/779914898687949694?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/Sc9rPOGdQpc/kirsqlite-flex-air-database-manager_18.html" title="KirSQLite - Flex AIR Database Manager: Part 17" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_18.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcERX8zfCp7ImA9WhVUEk4.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-5552354618336918223</id><published>2012-05-17T10:00:00.000+03:00</published><updated>2012-05-17T10:00:04.184+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-17T10:00:04.184+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 16</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/WxlYlcPaf3gBM_IDHszep42cYSg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WxlYlcPaf3gBM_IDHszep42cYSg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/WxlYlcPaf3gBM_IDHszep42cYSg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WxlYlcPaf3gBM_IDHszep42cYSg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll start creating the "Edit columns" tab.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Let's find the "Edit columns" NavigatorContent container in our code and start adding some content.&lt;br /&gt;
&lt;br /&gt;
First of all we'll add a HGroup, which is going to contain a List object and a VGroup container. Give the List an id of columnList, set its dataProvider bound to columnData, labelField to "name".&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;

&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The VGroup container will contain an HGroup object with 3 Buttons - "Add column", "Update selected" and "Delete selected", as well as a Form object, which will conist of 8 FormItems.&lt;br /&gt;
&lt;br /&gt;
The FormItems are labeled "Name", "Data type", "Primary Key", "Auto Increment", "Unique", "Allow Null", "Default Value" and "On Conflict". Set the content of these items as shown below:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="Add column" /&amp;gt;
&amp;lt;s:Button label="Update selected" /&amp;gt;
&amp;lt;s:Button label="Delete selected" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Form&amp;gt;
&amp;lt;mx:FormItem label="Name"&amp;gt;
&amp;lt;s:TextInput /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type"&amp;gt;
&amp;lt;s:ComboBox /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Auto Increment"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value"&amp;gt;
&amp;lt;s:TextArea/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;s:ComboBox /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to Declarations tags and declare this ArrayCollection object with an id columnData:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:ArrayCollection id="columnData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to the tableSelect() function. Here, we will actually set values to our columnData ArrayCollection. In the beginning of the function, set columnData to a blank ArrayCollection. Then in the second if... statement, find the for... loop which adds AdvancedDataGridColumns to a temporary array. Add a line that adds a new item to columnData with the name of the column in it.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="columnData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
columnData = new ArrayCollection([]);
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
columnData.addItem({name:schema.tables[0].columns[i].name});
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;
&amp;lt;s:HGroup width="100%" height="100%" &amp;gt;
&amp;lt;mx:List id="columnList" width="200" height="100%" dataProvider="{columnData}" labelField="name" /&amp;gt;
&amp;lt;s:VGroup height="100%" paddingTop="10"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="Add column" /&amp;gt;
&amp;lt;s:Button label="Update selected" /&amp;gt;
&amp;lt;s:Button label="Delete selected" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Form&amp;gt;
&amp;lt;mx:FormItem label="Name"&amp;gt;
&amp;lt;s:TextInput /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Data type"&amp;gt;
&amp;lt;s:ComboBox /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Primary Key"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Auto Increment"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Unique"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Allow Null"&amp;gt;
&amp;lt;s:CheckBox/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="Default Value"&amp;gt;
&amp;lt;s:TextArea/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label="On Conflict"&amp;gt;
&amp;lt;s:ComboBox /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-5552354618336918223?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/v2W__GjDC0w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/5552354618336918223/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_17.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/5552354618336918223?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/5552354618336918223?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/v2W__GjDC0w/kirsqlite-flex-air-database-manager_17.html" title="KirSQLite - Flex AIR Database Manager: Part 16" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_17.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0EESX48fip7ImA9WhVUEUg.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-7903042348250939851</id><published>2012-05-16T10:00:00.000+03:00</published><updated>2012-05-16T10:00:08.076+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-16T10:00:08.076+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 15</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/H9TKJqtBoETmWvXuJH8SWQH2Q-w/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/H9TKJqtBoETmWvXuJH8SWQH2Q-w/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/H9TKJqtBoETmWvXuJH8SWQH2Q-w/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/H9TKJqtBoETmWvXuJH8SWQH2Q-w/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll add the ability to add a new record to a table.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Find the HBox containing the "Delete selected" and "Save changes" buttons and add a new button next to them, called "Add a record". Set its click event handler to "newRecord()" and enabled property boudn to isTableSelected.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we'll create the newRecord() function.&lt;br /&gt;
&lt;br /&gt;
All we really need to do is insert a new row into the SQL using an SQLStatement and then reload the data to update the array and the data grid.&lt;br /&gt;
&lt;br /&gt;
The trick is that when we INSERT a record, we have to set a value under at least one column, or else we'll get an error. This can be done by adding "DEFAULT VALUES" to the SQL query instead of specifying values. Add a Responder object for the statement.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
stat.execute( -1, new Responder(newSuccess, newError));
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
On success, update the data. On error, display the error:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}

private function newRecord():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "INSERT INTO " + selectedDatabase + "." + tableTree.selectedItem.@label + " DEFAULT VALUES;";
stat.execute( -1, new Responder(newSuccess, newError));

function newSuccess(evt:SQLResult):void {
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
function newError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;s:Button id="newRecordButton" label="Add a record" click="newRecord();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-7903042348250939851?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/enpqxVFlCIo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/7903042348250939851/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_16.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/7903042348250939851?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/7903042348250939851?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/enpqxVFlCIo/kirsqlite-flex-air-database-manager_16.html" title="KirSQLite - Flex AIR Database Manager: Part 15" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_16.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UFR3k8eyp7ImA9WhVUEEs.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-2830893280005058084</id><published>2012-05-15T10:00:00.000+03:00</published><updated>2012-05-15T10:00:16.773+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-15T10:00:16.773+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 14</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Vtn-yaLT8QM2AJSXbw7KblaOQJY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Vtn-yaLT8QM2AJSXbw7KblaOQJY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Vtn-yaLT8QM2AJSXbw7KblaOQJY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Vtn-yaLT8QM2AJSXbw7KblaOQJY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll add the ability to delete rows from a table.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
The mechanic is simple - the user selects the rows he wants to remove using the CheckBoxes, then clicks the "Delete selected" button and all the selected rows are deleted from the database.&lt;br /&gt;
&lt;br /&gt;
Firstly find the "Delete selected" Button object and bind its enabled property to isTableSelected. Give it a click event handler deleteSelected():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Inside the function, the first thing we do is declare a variable keyColumnName and apply it the value of tableTree.selectedItem's primaryKeyColumn attribute (the same way we did it in saveTable()):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
After that, we loop through all tableData rows and check if their "sel" value is true. If it is, we create an SQLStatement object and construct a SQL query that deletes a row using the "DELETE" command and a "WHERE" conditional, which checks if the item's primary key column's value equals the one from the tableData array.&lt;br /&gt;
&lt;br /&gt;
We can use keyColumnName as the column name in the SQL ("WHERE " + keyColumnName), however, when we refer to the column name in the tableData array, we need to remember to put "db_" in front of the name (read previous tutorial to find out why!):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
After this, call tableSelect() to refresh data, and also call tableGrid's invalidateDisplayList() and invalidateList() methods to refresh the data grid.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
You can see that we added a Responder to the SQLStatement execution with success and error responders. Create these functions:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full deleteSelected() function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function deleteSelected():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
if (tableData[i].sel) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DELETE FROM " + selectedDatabase + "." + tableTree.selectedItem.@label + " WHERE " + keyColumnName + "=" + tableData[i]["db_" + keyColumnName];
stat.execute( -1, new Responder(deleteSuccess, deleteError));
}
}
tableSelect();
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();

function deleteSuccess(evt:SQLResult):void {
}
function deleteError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" enabled="{isTableSelected}" click="deleteSelected();" /&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-2830893280005058084?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/RMOWWDc_D3w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/2830893280005058084/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_15.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2830893280005058084?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2830893280005058084?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/RMOWWDc_D3w/kirsqlite-flex-air-database-manager_15.html" title="KirSQLite - Flex AIR Database Manager: Part 14" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_15.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8FRXY5cCp7ImA9WhVVGUo.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-8037837422161042474</id><published>2012-05-14T10:00:00.000+03:00</published><updated>2012-05-14T10:00:14.828+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-14T10:00:14.828+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 13</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/cG1XoMzhxDkqpNITuAzUm0VwzKI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cG1XoMzhxDkqpNITuAzUm0VwzKI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/cG1XoMzhxDkqpNITuAzUm0VwzKI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cG1XoMzhxDkqpNITuAzUm0VwzKI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll fix some problems with the current table data saving and loading code.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
There are currently two problems, and they are both related to the CheckBox column. The first problem is that if there are any items selected when the user presses the "Save changes" button, the selections are reset and some errors may be thrown out. The second problem is that if the table already has a column named "sel", the program misbehaves, because the "sel" property in the tableData array is meant to be associated with the selection CheckBox of that row.&lt;br /&gt;
&lt;br /&gt;
To fix the second problem, we'll need to put "db_" in front of each property in the tableData array that is taken from the SQL database. So this way, even if the table has a column "sel", it will be stored in the property "db_sel" of the array and will not conflict with the existing "sel" property that we use for CheckBoxes.&lt;br /&gt;
&lt;br /&gt;
Go to tableSelect() and find the loop that creates new AdvancedDataGridColumns. There, manually set the column's headerText to the column name from SQL, and the column's dataField to "db_" + the name from the SQL. After that, find the internal function of tableSelect(), which is called tableSuccess(). Here, put "db_" in front of the "value" in the line where attributes of the "obj" Object are set.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect():void {
saveTableButton.emphasized = false;
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to the saveTable() function. Here, in the beginning of the function, declare a temporary Array that will store the selection values. Loop through all tableData items to store their values in this temporary Array. We're doing this so that after reloading the data we can reload the selections too.&lt;br /&gt;
&lt;br /&gt;
Now, go below and look for the line that declares an if statement, which checks if an attribute in an element of tableData doesn't equal mx_internal_uid or keyColumnName. Add another statement here, check if attribute does not equal "sel". Inside of the loop itself, you'll have to use substr(3) on the "attribute" variable 3 times to get rid of the "db_" in front of it. Do this in places where you are refering to the parameter, or the column in the SQL:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Below the loop, find the line that adds the "WHERE" conditional to the sqlStat variable. Here, put "db_" in front of keyColumnName:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the very end of the function, create a loop that reloads and keeps the selection values in the array. After that, call invalidateDisplayList() and invalidateList() to refresh the data grid:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full function code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Problems are fixed. Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn();
aColumn.headerText = schema.tables[0].columns[i].name;
aColumn.dataField = "db_" + schema.tables[0].columns[i].name;
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj["db_"+value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// clear all "sel" and store them in a temp array
var tempSel:Array = [];
for (var s:int = 0; s &amp;lt; tableData.length; s++) {
if (tableData[s].sel) {
tempSel.push(true);
tableData[s].sel = false;
}else
if (!tableData[s].sel) {
tempSel.push(false);
}
}
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!=keyColumnName &amp;&amp; attribute!="sel") {
// add value as parameter
stat.parameters["@" + attribute.substr(3)] = tableData[i][attribute];
sqlStat += " " + attribute.substr(3) + "=@" + attribute.substr(3) + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i]["db_"+keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
for (var t:int = 0; t &amp;lt; tableData.length; t++) {
if (tempSel[t]) tableData[t].sel=true;
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-8037837422161042474?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/dAGSfEdX_dc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/8037837422161042474/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_14.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/8037837422161042474?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/8037837422161042474?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/dAGSfEdX_dc/kirsqlite-flex-air-database-manager_14.html" title="KirSQLite - Flex AIR Database Manager: Part 13" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_14.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMEQnk4eCp7ImA9WhVVGEQ.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-6552243098484663101</id><published>2012-05-13T10:00:00.000+03:00</published><updated>2012-05-13T10:00:03.730+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-13T10:00:03.730+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 12</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/pqvtUYtXWjn0uL_py8e6MzSl7D8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pqvtUYtXWjn0uL_py8e6MzSl7D8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/pqvtUYtXWjn0uL_py8e6MzSl7D8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pqvtUYtXWjn0uL_py8e6MzSl7D8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;Today we'll add the ability to change data in tables.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
First of all, find the first NavigatorContent container in the TabNavigator object. There, add a new Button labeled "Save changes" next to the "Delete selected" button. Set this button's id to saveTableButton. Set its click event handelr to saveTable(), and enabled property bound to isTableSelected.&lt;br /&gt;
&lt;br /&gt;
In the AdvancedDataGrid add an event listener for the itemEditBeing event, make the handler set saveTableButton's emphasized property to true. This will highlight the button when some data is edited in the table, telling the user that the changes have to be saved.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to the Tree object. Set its itemClick event handler to tableSelect() instead of tableSelect(event). We are making this function without the parameter:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We're going to do more changes to tableSelect() than just remove the parameter. Firstly, we'll set saveTableButton's emphasized property to false. Also, in the loop that adds AdvancedDataGridColumns to the newColumns array, add 2 if statements. The first one checks if the SQLColumnSchema object has autoIncrement property set to true. If it does, set editable property of this column to false. The second if statement checks if the column has its primaryKey property set to true. If it does, then set primaryKeyColumn attribute of the current table's XML node to the name of the column with primary key. Get rid of the parameter in the function, use tableTree instead of evt.currentTarget:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect():void {
saveTableButton.emphasized = false;
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we can create this saveTable() function. It is pretty complex, so I commented some sections.&lt;br /&gt;
&lt;br /&gt;
Firstly we create a variable keyColumnName to sture the primaryKeyColumn attribute value of the currently selected table. Then we loop through all the rows in the table. We create an SQLStatement object for each of it, as well as a sqlStat String variable that we'll use to compose a statement.&lt;br /&gt;
&lt;br /&gt;
Inside this loop, we add another loop that goes through all the attributes of the current row object in the ArrayCollection and adds items to the parameters array of our SQLStatement object. This is needed to prevent any errors that might happen if the values that we're going to pass in the SQL query are not properly isolated. So, we set attributes with the same names to the parameters object along with their values, and we also add "attribute=@attribute," (where "attribute" is the anme of the attribute) to the sqlStat text variable.&lt;br /&gt;
&lt;br /&gt;
After this loop, we delete the last letter of the sqlStat String to remove the last unneeded comma, and then we add a "WHERE" conditional to the SQL query and look for items with the same id. This way, we edit the data of the needed item by finding it by its unique id.&lt;br /&gt;
&lt;br /&gt;
Then we just execute the statement and in case there were any errors caught, we display them.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!= keyColumnName) {
// add value as parameter
stat.parameters["@" + attribute] = tableData[i][attribute];
sqlStat += " " + attribute + "=@" + attribute + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i][keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect():void {
saveTableButton.emphasized = false;
if (tableTree.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (tableTree.selectedItem.@numid == 1) dataname = "main";
if (tableTree.selectedItem.@numid &amp;gt; 1) dataname = "db" + tableTree.selectedItem.@numid;
selectedDatabase = dataname;
}
if (tableTree.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = tableTree.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, tableTree.selectedItem.@label, tableTree.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
if (schema.tables[0].columns[i].autoIncrement) aColumn.editable = false;
if (schema.tables[0].columns[i].primaryKey) tableTree.selectedItem.@primaryKeyColumn = schema.tables[0].columns[i].name;
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + tableTree.selectedItem.@databaseName + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}

private function saveTable():void {
var keyColumnName:String = tableTree.selectedItem.@primaryKeyColumn;
// update each row
for (var i:int = 0; i &amp;lt; tableData.length; i++) {
var stat:SQLStatement = new SQLStatement();
var sqlStat:String = "UPDATE " + selectedDatabase + "." + tableTree.selectedItem.@label + " SET";
// add each attribute as parameter
for (var attribute:String in tableData[i]) {
// if column is not our CheckBox column or the key column
if (attribute != "mx_internal_uid" &amp;&amp; attribute!= keyColumnName) {
// add value as parameter
stat.parameters["@" + attribute] = tableData[i][attribute];
sqlStat += " " + attribute + "=@" + attribute + ",";
}
}
// remove the last comma
sqlStat = sqlStat.substr(0, sqlStat.length - 1);
sqlStat += " WHERE " + keyColumnName + "=" + tableData[i][keyColumnName];
stat.sqlConnection = connection;
stat.text = sqlStat;
stat.execute( -1, new Responder(saveSuccess, saveError));
}

function saveSuccess(evt:SQLResult):void {
}

function saveError(evt:SQLError):void {
Alert.show("ERROR: " + evt.details, "Error");
}

tableSelect();
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect();"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;s:Button id="saveTableButton" label="Save changes" click="saveTable();" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true" itemEditBegin="saveTableButton.emphasized=true;"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-6552243098484663101?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/reKzYOvulH4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/6552243098484663101/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_13.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6552243098484663101?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6552243098484663101?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/reKzYOvulH4/kirsqlite-flex-air-database-manager_13.html" title="KirSQLite - Flex AIR Database Manager: Part 12" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_13.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcEQnwzeyp7ImA9WhVVGE0.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-4047179804892606744</id><published>2012-05-12T10:00:00.000+03:00</published><updated>2012-05-12T10:00:03.283+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-12T10:00:03.283+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 11</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Nids-QWnsp0hfyegWRTp8VG607o/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Nids-QWnsp0hfyegWRTp8VG607o/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Nids-QWnsp0hfyegWRTp8VG607o/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Nids-QWnsp0hfyegWRTp8VG607o/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll sort out the native menu by assigning some of the existing features to the menu items. We'll also add an ability to save a copy of a database (backup).&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Firstly, go to the windowMenu XML object and edit its contents by adding a "Save a copy" menu item to the existing two under the "Database" menu. Set the subchildren of the "Table" menu to two items - "Add table" and "Drop table". Set their shortcuts, enabled and id properties as shown below:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to menuSelect and assign handler functions for each of the menu items. Set the "newtable" and "droptable" handlers to existing newTable() and dropTable() functions, and the new "saveddb" item's handler to a new function saveCopy():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The function first finds out the path of the current database. The selectedDatabase value contains the sql name of the database. If the selectedDatabase's value is "main", then it's easy - the database is the first one in the dbData xml.&lt;br /&gt;
&lt;br /&gt;
If the value is not main, it consists of "db" with a number, for example, "db3". We can take the "db" away to get the number and find which database item in dbData has the same numid attribute value as this number. Then we use the index of that item in the XML to find out the path to this database:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
After that, we create a File object to open a new browse window to let the user select the destination of the copy. After it is selected, we check if the selected file is already opened in our application. If it is, abort operation, because this might raise problems. Create a new File object of the initial database and use its copyTo() method to copy the file:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full function code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The feature we added today will be useful to developers to be able to quickly backup databases before attempting to do anything with them.&lt;br /&gt;
&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="savedb" label="Save a copy" key="s" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem id="newtable" label="Add table" key="t" controlKey="true" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;menuitem id="droptable" label="Drop table" key="d" controlKey="true" enabled="{isTableSelected}"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.Responder;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
private var sqlHistory:Array = [];

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
(evt.item.@id == "newtable")?(newTable()):(void);
(evt.item.@id == "droptable")?(dropTable()):(void);
(evt.item.@id == "savedb")?(saveCopy()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function saveCopy():void {
var databasePath:String;
if (selectedDatabase == "main") databasePath = dbData.db[0].@path;
if (selectedDatabase != "main") {
var newNum:int = Number(selectedDatabase.replace("db", ""));
var newInd:int;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (dbData.db[i].@numid == newNum) {
newInd = i;
break;
}
}
databasePath = dbData.db[newInd].@path;
}
var file:File = new File(databasePath);
file.browseForSave("Save copy of database");
file.addEventListener(Event.SELECT, onCopySelect);
function onCopySelect(evt:Event):void {
if(notAlreadyOpen(file)){
var initFile:File = new File(databasePath);
initFile.copyTo(file, true);
}else {
Alert.show("Cannot overwrite a file that is currently open.", "Nope");
}
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (evt.currentTarget.selectedItem.@numid == 1) dataname = "main";
if (evt.currentTarget.selectedItem.@numid &amp;gt; 1) dataname = "db" + evt.currentTarget.selectedItem.@numid;
selectedDatabase = dataname;
}
if (evt.currentTarget.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = evt.currentTarget.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
lastStatement(stat.text);
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
focusManager.setFocus(newTableName);
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
lastStatement(stat.text);
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
lastStatement(stat.text);
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}

private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}

private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect(event);"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-4047179804892606744?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/vEyWYiIl0Ww" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/4047179804892606744/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_12.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/4047179804892606744?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/4047179804892606744?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/vEyWYiIl0Ww/kirsqlite-flex-air-database-manager_12.html" title="KirSQLite - Flex AIR Database Manager: Part 11" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_12.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EFRXc7eCp7ImA9WhVVF0w.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-163222820812152993</id><published>2012-05-11T10:00:00.000+03:00</published><updated>2012-05-11T10:00:14.900+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-11T10:00:14.900+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 10</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/lN3BtZRKBQKsIljNB9wUkniB6-I/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/lN3BtZRKBQKsIljNB9wUkniB6-I/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/lN3BtZRKBQKsIljNB9wUkniB6-I/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/lN3BtZRKBQKsIljNB9wUkniB6-I/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll add a field that will display the latest executed SQL Statement to the user.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
This feature may prove useful for many reasons. For example, the user might need to track the commands to be able to undo them. Or he'd like to check the database names before writing a manual SQL query. This will also be very useful to the developers (us!) for debugging.&lt;br /&gt;
&lt;br /&gt;
Go to the VGroup with the TabNavigator in it and place this Box above the TabNavigator:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Box height="80" width="100%"&amp;gt;
&amp;lt;s:VGroup paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10" width="100%" height="100%"&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Latest SQL statement:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Button width="100" label="View history" click="openHistory();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:TextArea id="statementText" editable="false" width="100%" height="30"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
As you can see, the user sees a TextArea with an id of statementText, as well as a label and a button labeled "View history". The click event handler of the button is set to openHistory().&lt;br /&gt;
&lt;br /&gt;
Declare a new array, sqlHistory:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private var sqlHistory:Array = [];
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Add a new function lastStatement(), which receives a String value of the statement.&lt;br /&gt;
&lt;br /&gt;
The function sets statementText's text to this value, and pushes it to sqlHistory array:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function lastStatement(text:String):void {
statementText.text = text;
sqlHistory.push(text);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we call this function and pass the statement query text as the parameter each time we execute an SQLStatement object. You'll see it in the full code in the end of this tutorial.&lt;br /&gt;
&lt;br /&gt;
Now, create a new TitleWindow in the Declarations tags. Give it an id of historyWindow, set its close event handler to closeHistoryWindow() and add a TextArea with an id "historyText".&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:TitleWindow id="historyWindow" title="SQL History" close="closeHistoryWindow();" showCloseButton="true"&amp;gt;
&amp;lt;mx:Box width="100%" height="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10"&amp;gt;
&amp;lt;s:TextArea id="historyText" width="100%" height="100%" editable="false" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now create the openHistory() function. Here, we add the pop up, set its size, center it and loop through the array's values to add them to the text area:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function openHistory():void {
PopUpManager.addPopUp(historyWindow, this);
historyWindow.width = width - 100;
historyWindow.height = height - 100;
PopUpManager.centerPopUp(historyWindow);
historyText.text = "";
for (var i:int = sqlHistory.length - 1; i &amp;gt;= 0; i--) {
historyText.appendText(sqlHistory[i] + "\n");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The closeHistoryWindow() function closes the window:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function closeHistoryWindow():void {
PopUpManager.removePopUp(historyWindow);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-163222820812152993?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/R6CacPmJfDE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/163222820812152993/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_11.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/163222820812152993?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/163222820812152993?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/R6CacPmJfDE/kirsqlite-flex-air-database-manager_11.html" title="KirSQLite - Flex AIR Database Manager: Part 10" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_11.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUFQHg_fCp7ImA9WhVVFk4.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-7153645563454255558</id><published>2012-05-10T10:00:00.000+03:00</published><updated>2012-05-10T10:00:11.644+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-10T10:00:11.644+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 9</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Udqypu29pi2n9_r8RkA8qywYB3U/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Udqypu29pi2n9_r8RkA8qywYB3U/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Udqypu29pi2n9_r8RkA8qywYB3U/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Udqypu29pi2n9_r8RkA8qywYB3U/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we are going to add the ability to add new tables to a database, and to drop tables.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Find the VGroup that our Tree object is located in. Add a HGroup above the Tre with two buttons in it, labeled "New table" and "Drop table". Set their click event handlers to newTable() and dropTable(). Set their enabled properties to {table.selectedItems.length&amp;gt;0} and {isTableSelected}.&lt;br /&gt;
&lt;br /&gt;
Set the id of our table to tableTree.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect(event);"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We'll need to declare two variables - selectedDatabase and isTableSelected.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The selectedDatabase variable must contain the name of the current database ("main", or "db"+index).&lt;br /&gt;
&lt;br /&gt;
Go to tableSelect() function. Add another if statement that executes code if the selected item in the tree is a branch. Inside of the statement, set isTableSelected to false, and add code that sets selectedDatabase to the proper value, based on the "numid" attribute of the selected database.&lt;br /&gt;
&lt;br /&gt;
In the second if statement, set isTableSelected to false and selectedDatabase to "databaseName" attribute of the selected tree item.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (evt.currentTarget.selectedItem.@numid == 1) dataname = "main";
if (evt.currentTarget.selectedItem.@numid &amp;gt; 1) dataname = "db" + evt.currentTarget.selectedItem.@numid;
selectedDatabase = dataname;
}
if (evt.currentTarget.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = evt.currentTarget.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Go to loadDataSchema() and set isTableSelected to false:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now that the values of these two variables are set properly, we can use them to create and drop tables.&lt;br /&gt;
&lt;br /&gt;
First create the newTable() function. Here, we will call out a pop up window called newTableWindow, where the user will be able to enter the name for the new table:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The newTableWindow object is a TitleWindow that is declared in the Declarations tags. Set its close event listener to closeNewTableWindow(), set showCloseButton to true, and inside of the TitleWindow add a TextInput with an id "newTableName" and button with a click event handler "createNewTable()":&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The closeNewTableWindow() closes the window:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The createNewTable() function creates a new SQLStatement object with a query built using our existing data. It is important that we set both success and error event handlers using a responder for this query, and display the error details to the user if an error occured. If everything went smoothly, close the window and call loadDataSchema():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The Drop Table feature is more simple. First we throw out an Alert, asking the user if he is sure that he wants to remove the table. If the answer is YES, we create a new SQLStatement that drops the table. Once again, if there was an error - tell the user the details of the error. Otherwise, call loadDataSchema():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:TitleWindow id="newTableWindow" title="Create new table" close="closeNewTableWindow();" showCloseButton="true"&amp;gt;
&amp;lt;s:VGroup&amp;gt;
&amp;lt;s:HGroup width="100%" verticalAlign="middle"&amp;gt;
&amp;lt;s:Label&amp;gt;Table name: &amp;lt;/s:Label&amp;gt;
&amp;lt;s:TextInput id="newTableName" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:Button click="createNewTable();" label="Create" width="100%" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.Responder;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexNativeMenuEvent;
import mx.managers.PopUpManager;

private var connection:SQLConnection = new SQLConnection();
private var selectedDatabase:String = "";
[Bindable]
private var isTableSelected:Boolean = false;

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
isTableSelected = false;
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch) {
isTableSelected = false;
var dataname:String;
if (evt.currentTarget.selectedItem.@numid == 1) dataname = "main";
if (evt.currentTarget.selectedItem.@numid &amp;gt; 1) dataname = "db" + evt.currentTarget.selectedItem.@numid;
selectedDatabase = dataname;
}
if (evt.currentTarget.selectedItem.@isBranch == false) {
isTableSelected = true;
selectedDatabase = evt.currentTarget.selectedItem.@databaseName;
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}

private function newTable():void {
PopUpManager.addPopUp(newTableWindow, this);
PopUpManager.centerPopUp(newTableWindow);
newTableWindow.title = "Create new table";
}

private function dropTable():void {
Alert.show("Are you sure you want to completely delete this table?", "Drop table?", Alert.YES | Alert.NO, null, dropConfirm);
function dropConfirm(evt:CloseEvent):void {
if (evt.detail == Alert.YES) {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "DROP TABLE " + selectedDatabase + "." + tableTree.selectedItem.@label;
stat.execute( -1, new Responder(dropTableSuccess, dropTableError));
}
}
function dropTableSuccess(evt:SQLResult):void {
loadDataSchema(selectedDatabase);
}
function dropTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}

private function closeNewTableWindow():void{
PopUpManager.removePopUp(newTableWindow);
}

private function createNewTable():void {
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "CREATE TABLE IF NOT EXISTS " + selectedDatabase + "." + newTableName.text + "(id INTEGER PRIMARY KEY AUTOINCREMENT, blankColumn TEXT)";
stat.execute( -1, new Responder(newTableSuccess, newTableError));
function newTableSuccess(evt:SQLResult):void {
closeNewTableWindow();
loadDataSchema(selectedDatabase);
}
function newTableError(evt:SQLError):void {
Alert.show("ERROR:" + evt.details, "Error");
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button label="New table" click="newTable();" enabled="{tableTree.selectedItems.length&amp;gt;0}"/&amp;gt;
&amp;lt;s:Button label="Drop table" click="dropTable();" enabled="{isTableSelected}" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;mx:Tree id="tableTree" width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect(event);"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-7153645563454255558?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/G1TBWhcvssU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/7153645563454255558/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_10.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/7153645563454255558?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/7153645563454255558?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/G1TBWhcvssU/kirsqlite-flex-air-database-manager_10.html" title="KirSQLite - Flex AIR Database Manager: Part 9" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_10.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8FQno5fCp7ImA9WhVVFUk.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-6810020136004242037</id><published>2012-05-09T10:00:00.000+03:00</published><updated>2012-05-09T10:00:13.424+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-09T10:00:13.424+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 8</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ge-eFy1fWRAogGIOxxCGiNMCh8U/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ge-eFy1fWRAogGIOxxCGiNMCh8U/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ge-eFy1fWRAogGIOxxCGiNMCh8U/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ge-eFy1fWRAogGIOxxCGiNMCh8U/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;Today we'll add a feature to our SQLite manager that reads and parses table data.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
The function we'll work in today is the tableSelect() function.&lt;br /&gt;
&lt;br /&gt;
Last time we added code that displays and assigns columns of the table to the advanced data grid. Right after the last line of that piece of code, add a new SQLStatement object. Set its sqlConnection property's value to our connection object. Set the query text to a SELECT command, which selects everything from the currently selected table from its parent database. Execute the statement and set a responder.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
stat.execute(-1, new Responder(tableSuccess, tableError));
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This piece of code is included in the if statement that we currently have in tableSelect(). In the beginning of this if statement, add a line which sets tableData to a new blank ArrayCollection.&lt;br /&gt;
&lt;br /&gt;
So you'll get this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (evt.currentTarget.selectedItem.@isBranch == false) {
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
stat.execute(-1, new Responder(tableSuccess, tableError));
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we need to create the tableSuccess and tableError functions.&lt;br /&gt;
&lt;br /&gt;
The second one is very simple - we just alert the user that an error has occured.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The tableSuccess() function is more complex.&lt;br /&gt;
&lt;br /&gt;
First, we check if evt.data is not null.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {

}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then we loop through all properties in evt.data - the object that is filled with data that was read from the sql:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {

}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Inside of it, we create a new "obj" Object variable, which represents one row in the table. Then we add another loop that loops through all attributes in the current row's Object. We use the variable that we used in declaring that last loop to read the name of the attribute and its value. We set the "obj" Object's attribute with the same attribute name as in SQL (so that it matched column name in AdvancedDataGrid) and apply the value from the SQL to this object.&lt;br /&gt;
&lt;br /&gt;
Then we simply add this "obj" Object to the tableData ArrayCollection:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch == false) {
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now you can select tables and see their contents!&lt;br /&gt;
&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.Responder;
import mx.collections.ArrayCollection;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;

private var connection:SQLConnection = new SQLConnection();

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch == false) {
tableData = new ArrayCollection([]);
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
var stat:SQLStatement = new SQLStatement();
stat.sqlConnection = connection;
stat.text = "SELECT * FROM " + evt.currentTarget.selectedItem.@databaseName + "." + evt.currentTarget.selectedItem.@label;
stat.execute(-1, new Responder(tableSuccess, tableError));
}
function tableSuccess(evt:SQLResult):void {
if (evt.data != null) {
for (var item:Object in evt.data) {
var obj:Object = new Object();
for (var value:Object in evt.data[item]) {
obj[value] = evt.data[item][value];
}
tableData.addItem(obj);
}
}
}
function tableError(evt:SQLError):void {
Alert.show("Unable to read table data.", "Error");
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect(event);" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-6810020136004242037?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/o9lq5U9eEDg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/6810020136004242037/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_09.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6810020136004242037?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6810020136004242037?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/o9lq5U9eEDg/kirsqlite-flex-air-database-manager_09.html" title="KirSQLite - Flex AIR Database Manager: Part 8" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_09.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMESHk_cCp7ImA9WhVVFEs.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-6044764319249113851</id><published>2012-05-08T10:00:00.000+03:00</published><updated>2012-05-08T10:00:09.748+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-08T10:00:09.748+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 7</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ZlsJLhstjmfXjczcaCaWfWfN8VA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ZlsJLhstjmfXjczcaCaWfWfN8VA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ZlsJLhstjmfXjczcaCaWfWfN8VA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ZlsJLhstjmfXjczcaCaWfWfN8VA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this part of the tutorial we will add the ability to view columns of a table by selecting a table in the tree container and also add a check that doesn't let the user create a new database file on top of another one, overwriting it.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
To prevent overwriting, add an if statement to the newDatabase() function, that checks if the file already exists:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The columns will be displayed in the tableGrid AdvancedDataGrid object. As you may already know, to assign column values to an AdvancedDataGrid, you have to create an array and apply it to the "columns" property of the data grid object.&lt;br /&gt;
&lt;br /&gt;
Find the AdvancedDataGrid in the existing code and set its columns property to an array of one single AdvancedDataGridColumn object, which has the header "Data". That's what will be displayed by default.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We had a column that was supposed to contain checkboxes for each item, so that the user can select multiple items to perform an action with them. The column is not lost, just move it to the Declarations tags and give it an id of "checkboxColumn", we'll use it soon.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to the loadDataSchema() function. Here we'll add 2 more attributes to each table element in the XML. The attributes are isBranch and databaseName. Set their values as shown:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now find the Tree object. Add an "itemClick" event listener and set its handler to tableSelect(event):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect(event);" /&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The function first checks if the selected item is not a branch. If it isn't, then its a table. Create a new array with just one value in the beginning (checkboxColumn), and then use connection's loadSchema() object to load data of the table. Set values to the first 3 parameters of the function as shown below.&lt;br /&gt;
&lt;br /&gt;
After that is done, we can use the schema's data to loop through the table's columns and add them one by one to the array. Then we just apply the array to tableGrid.columns:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch == false) {
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn id="checkboxColumn" headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.Responder;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;

private var connection:SQLConnection = new SQLConnection();

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
if (file.exists) {
Alert.show("File already exists, cannot overwrite.", "Nope");
return;
}
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
newTable.@isBranch = false;
newTable.@databaseName = name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}

private function tableSelect(evt:Event):void {
if (evt.currentTarget.selectedItem.@isBranch == false) {
var newColumns:Array = [checkboxColumn];
connection.loadSchema(SQLTableSchema, evt.currentTarget.selectedItem.@label, evt.currentTarget.selectedItem.@databaseName);
var schema:SQLSchemaResult = connection.getSchemaResult();
for (var i:int = 0; i &amp;lt; schema.tables[0].columns.length; i++) {
var aColumn:AdvancedDataGridColumn = new AdvancedDataGridColumn(schema.tables[0].columns[i].name);
newColumns.push(aColumn);
}
tableGrid.columns = newColumns;
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label" itemClick="tableSelect(event);" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="" headerText="Data" editable="false" /&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-6044764319249113851?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/LkAuWER0tNM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/6044764319249113851/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_08.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6044764319249113851?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6044764319249113851?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/LkAuWER0tNM/kirsqlite-flex-air-database-manager_08.html" title="KirSQLite - Flex AIR Database Manager: Part 7" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_08.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcEQnw_fCp7ImA9WhVVE0o.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-947719538105426228</id><published>2012-05-07T10:00:00.000+03:00</published><updated>2012-05-07T10:00:03.244+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-07T10:00:03.244+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 6</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/J3Uv8MM6gP9isI9aODnwlJ6FYDQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/J3Uv8MM6gP9isI9aODnwlJ6FYDQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/J3Uv8MM6gP9isI9aODnwlJ6FYDQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/J3Uv8MM6gP9isI9aODnwlJ6FYDQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this part we'll improve our SQL Schema reading function.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
In the last tutorial, we've created a function called loadDataSchema(). Here, we call the loadSchema() method of our SQLConnection object twice - the first time to see if the schema exists, the second time to read the data.&lt;br /&gt;
&lt;br /&gt;
When we call it the first time, we set a responder for success and error events. If the operation has succeeded, only then we proceed to load the schema for the second time. We did this, because we couldn't read the schema using getSchemaResult() method. But there's a more efficient and quicker way to read the schema data, without loading it twice.&lt;br /&gt;
&lt;br /&gt;
We simply assign the results variable to the event object that was received:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, after the schema is received, we can read its data to add tables as children for the approporiate database nodes in the dbData XMLList. To find out which node we're looking for, let's break the "name" value we have into a number, which will represent the "numid" attribute of the database in the XMLList.&lt;br /&gt;
&lt;br /&gt;
Create a variable and set its value depending on what the "name" is. If the name is set to "main", then the number will equal 1. Otherwise (if the name is "db2", "db3", etc), remove "db" from the name to get the needed number (2, 3, etc).&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then we look for that node in the XMLList by checking whether the "numid" attribute's value equals "nid":&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var dataNode:XMLList = dbData.db.(@numid == nid);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We need to make sure the node has no children before we add anything to it. To do that, simply set the children of this node to a single placeholder item, and then delete that item:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
After that, we can create a loop that adds XML children to the dataNode object. Set the label values of the children to the "name" value of its respective SQLTableSchema object in the result.tables array.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
dataNode.appendChild(newTable);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full loadDataSchema() function:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The application now displays all tables in an SQLite database.&lt;br /&gt;
&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.Responder;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;

private var connection:SQLConnection = new SQLConnection();

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
var result:SQLSchemaResult = evt;
// Adding tables:
var nid:Number = (name=="main")?(1):(Number(name.replace("db", "")));
var dataNode:XMLList = dbData.db.(@numid == nid);
dataNode.setChildren(&amp;lt;placeholder/&amp;gt;);
delete dataNode.placeholder;
for (var i:int = 0; i &amp;lt; result.tables.length; i++) {
var newTable:XML = new XML(&amp;lt;tb/&amp;gt;);
newTable.@label = result.tables[i].name;
dataNode.appendChild(newTable);
}
}
function schemaError(evt:SQLError):void {
// Alert.show("Database is empty");
}
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" editable="false" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-947719538105426228?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/rf03NCMHlwo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/947719538105426228/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_07.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/947719538105426228?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/947719538105426228?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/rf03NCMHlwo/kirsqlite-flex-air-database-manager_07.html" title="KirSQLite - Flex AIR Database Manager: Part 6" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_07.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUEFQXg8eip7ImA9WhVVEkU.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-2480524299454939074</id><published>2012-05-06T10:00:00.000+03:00</published><updated>2012-05-06T10:00:10.672+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-06T10:00:10.672+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 5</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ykvlKwKjROUzEWFLpdojowG-sTw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ykvlKwKjROUzEWFLpdojowG-sTw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ykvlKwKjROUzEWFLpdojowG-sTw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ykvlKwKjROUzEWFLpdojowG-sTw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this part of the tutorial we'll begin to read the structure of the opened SQLite database.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
First of all, let's optimize the code we have right now. We can do that by putting some of the code from openDatabase() and newDatabase() functions into a separate function called parseDatabase(), which receives 2 parameters - the File object of the database and a boolean needCheck. The function basically opens the database and parses it. If needCheck is set to true, it also checks if this database exists before continuing.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we can replace those repeating lines in newDatabase() and openDatabase() by this function.&lt;br /&gt;
&lt;br /&gt;
You might have noticed that the function returns a string value. This value is the name of the newly created database in our connection object. We use this value to pass as a parameter when we call loadDataSchema() function - a function that is called right after parseDatabase() to load and parse the content of the database.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
This is the part where it got tricky for me. To find out information about inner content of the databases such as tables and indices, we need to load a schema. In AIR this is done using loadSchema() method. However, the schema does not exist if the databse is empty. That is why we first load the schema to add 2 event listeners - one function is called if the operation was successful (the schema exists), and the other one will be called if there is an error (the schema does not exist).&lt;br /&gt;
&lt;br /&gt;
We pass a bunch of parameters to the loadSchema() method, including the name of the database we're loading and a Responder object containing references to success and error functions.&lt;br /&gt;
&lt;br /&gt;
In the success function, we'll load the schema one more time, then read it using getSchemResult() method, and display the number of tables in the database. The schema needs to be loaded again before we read the results.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
connection.loadSchema(null, null, name, true);
var result:SQLSchemaResult = connection.getSchemaResult();
Alert.show("Schema found! Tables in this database: " + result.tables.length);
}
function schemaError(evt:SQLError):void {
Alert.show("Database is empty");
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we can open databases and view how many tables it contains.&lt;br /&gt;
&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLResult;
import flash.data.SQLSchema;
import flash.data.SQLSchemaResult;
import flash.data.SQLStatement;
import flash.errors.SQLError;
import flash.events.Event;
import flash.events.SQLEvent;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.Responder;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;

private var connection:SQLConnection = new SQLConnection();

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
file.nativePath += ".db";
var n:String = parseDatabase(file);
loadDataSchema(n);
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db"), new FileFilter("All files", "*")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
var n:String = parseDatabase(file, true);
loadDataSchema(n);
}
}

private function loadDataSchema(name:String):void {
if (name != "") {
connection.loadSchema(null, null, name, true, new Responder(schemaSuccess, schemaError));
function schemaSuccess(evt:SQLSchemaResult):void {
// Schema found! Now parsing:
connection.loadSchema(null, null, name, true);
var result:SQLSchemaResult = connection.getSchemaResult();
Alert.show("Schema found! Tables in this database: " + result.tables.length);
}
function schemaError(evt:SQLError):void {
Alert.show("Database is empty");
}
}
}

private function parseDatabase(file:File, needCheck:Boolean = false):String {
var ret:String = "";
if (!needCheck || file.exists) {
if(!needCheck || notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "main";
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
ret = "db" + newnum.toString();
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
return ret;
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" editable="false" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-2480524299454939074?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/a6nrr7tC904" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/2480524299454939074/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_06.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2480524299454939074?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2480524299454939074?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/a6nrr7tC904/kirsqlite-flex-air-database-manager_06.html" title="KirSQLite - Flex AIR Database Manager: Part 5" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_06.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UERXg7eSp7ImA9WhVVEk0.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-5713515027632027827</id><published>2012-05-05T10:00:00.000+03:00</published><updated>2012-05-05T10:00:04.601+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-05T10:00:04.601+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 4</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/YXW68gIFzGoVBgd9MQdiuJsF6ZA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YXW68gIFzGoVBgd9MQdiuJsF6ZA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/YXW68gIFzGoVBgd9MQdiuJsF6ZA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YXW68gIFzGoVBgd9MQdiuJsF6ZA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this part we'll add the ability to open databases that are stored on the hard drive.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Firstly go to the windowMenu XML and set the id for the "Open" XML menuitem to "newdb":&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now in the menuSelect() function add a new line that responds to the user's selection of the "Open" option in the menu:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
As you can see, I call an openDatabase() function.&lt;br /&gt;
&lt;br /&gt;
Inside of openDatabase(), I first create a new File object and call its browseForOpen() method. I create a FileFilter object to filter database (.db) files. Then I add an Event.SELECT listener to the file:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db")]);
file.addEventListener(Event.SELECT, openSelect);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The openSelect() function checks if the file exists. If it doesn't, throw out an Alert, saying that the file was not found.&lt;br /&gt;
&lt;br /&gt;
Then check if the file is already open using notAlreadyOpen() function that I'll create later. If the file is already open, throw an alert saying "Database already opened".&lt;br /&gt;
&lt;br /&gt;
If the file exists and not already opened, check if dbData's length equals 0. If it does, open the database using the connection's open() method. Add the XML node to dbData as usual. If dbData's length is more than 0, use the connection's attach() method. Everything here is similar to what happens in newDatabase()...&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
if (file.exists) {
if(notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
}
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem id="opendb" label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.events.Event;
import flash.filesystem.File;
import flash.net.FileFilter;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;

private var connection:SQLConnection = new SQLConnection();

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
(evt.item.@id == "opendb")?(openDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
file.nativePath += ".db";
if (dbData.db.length() == 0) {
connection.open(file);
statement.sqlConnection = connection;
statement.text = "CREATE TABLE IF NOT EXISTS main.test (id INTEGER PRIMARY KEY AUTOINCREMENT)";
statement.execute();
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
statement.sqlConnection = connection;
statement.text = "CREATE TABLE IF NOT EXISTS db" + newnum.toString() + ".test (id INTEGER PRIMARY KEY AUTOINCREMENT)";
statement.execute();
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
}
}
}

private function openDatabase():void {
var file:File = new File();
file.browseForOpen("Open database", [new FileFilter("Databases", "*.db")]);
file.addEventListener(Event.SELECT, openSelect);

function openSelect(evt:Event):void {
if (file.exists) {
if(notAlreadyOpen(file)){
var newDB:XML;
if (dbData.db.length() == 0) {
connection.open(file);
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
newDB.@path = file.nativePath;
dbData[0].appendChild(newDB);
}}else {
Alert.show("Database already opened.", "Error");
}
}else {
Alert.show("File not found.", "Error");
}
}
}

private function notAlreadyOpen(file:File):Boolean{
var r:Boolean = true;
for (var i:int = 0; i &amp;lt; dbData.db.length(); i++) {
if (file.nativePath == dbData.db[i].@path) {
r = false;
}
}
return r;
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected"/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" editable="false" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-5713515027632027827?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/Hj8JtEC4Xjs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/5713515027632027827/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_05.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/5713515027632027827?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/5713515027632027827?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/Hj8JtEC4Xjs/kirsqlite-flex-air-database-manager_05.html" title="KirSQLite - Flex AIR Database Manager: Part 4" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_05.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk8ESX04fyp7ImA9WhVVEUw.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-8064350059582633894</id><published>2012-05-04T10:00:00.000+03:00</published><updated>2012-05-04T10:00:08.337+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-04T10:00:08.337+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 3</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sPjx7BCt1rlThQXQkFdaWRKbWmY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sPjx7BCt1rlThQXQkFdaWRKbWmY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sPjx7BCt1rlThQXQkFdaWRKbWmY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sPjx7BCt1rlThQXQkFdaWRKbWmY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will add some very basic functionality to our program. We will add the ability to create new databases and save them.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
First of all we'll nede to get rid of the 2 placeholder data holders in the Declarations tags - testData and testTree. Create new XMLList and ArrayCollection in their place, name them dbData and tableData. Also, Set the first menu item's "New" id to "newdb" in the windowMenu XML:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now find the FlexNativeMenu declaration line and set its itemClick event handler to menuSelect(event):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Before we continue, let's get rid of the temporary data objects' traces. Change the Tree object's dataProvider to dbData:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label"/&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
And AdvancedDataGrid's to tableData:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to the script tags and in the selectAllChange() function change all "testData" to "tableData":&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now declare a connection variable:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private var connection:SQLConnection = new SQLConnection();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Create a new function menuSelect(), which checks the selected item's id. If it is "newdb", call newDatabase():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the newDatabase() function first of all we create a new File object and call its browseForSave() method. We set a listener for its Event.SELECT event and declare 2 variables - newDB (an XML) and statement (an SQLStatement):&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now inside the function, we add another function - newSelect(). This one is called when the user has selected the save location.&lt;br /&gt;
&lt;br /&gt;
Inside of it, we add ".db" to the end of the file's native path.&lt;br /&gt;
&lt;br /&gt;
Then we check if this is the first database that is opened. If it is, we use the connection's open() method and pass the file object as the parameter. We create a temporary SQL statement to create a new blank table - just for the test. You won' be able to see the table in this tutorial, but you'll see that the size of the database on hard drive increased, which means that the table was added.&lt;br /&gt;
&lt;br /&gt;
After the statement is executed, we set dbData to a new XMLList() object, which contains just root tags. After the root tags are added, we use the "newDB" variable to create a new XML element and add it to the XMLList. We specify the following properties for the object: label, numid and isBranch. Set label to file.name, numid to 1 and isBranch to true. Then append the XML child to the dbData object.&lt;br /&gt;
&lt;br /&gt;
Now, this all hapepns when there were no databases previously opened. If the dbData object is not empty, then we execute a little bit different code.&lt;br /&gt;
&lt;br /&gt;
First of all we declare a "newnum" variable which holds the new "numid" for the object (this property will be needed later to tell apart the databases). Newnum's value is set to the current number of databases opened +1. &lt;br /&gt;
&lt;br /&gt;
Instead of calling connection's open() method, we call an attach() method, specify a name ("db" + newnum) and the file object as parameters.&lt;br /&gt;
&lt;br /&gt;
Then we execute an SQL statement. This statement is the same as the previous one, except that the database which we create the table in is different. We can refer to the first database using main. prefix, and to all other databases using its name with a dot (for example, "db2."). After the dot we specify the table name, for example, "main.test" will create a "test" table in the main (first) database.&lt;br /&gt;
&lt;br /&gt;
After the statement, we create a new XML object to append to the dbData object.&lt;br /&gt;
&lt;br /&gt;
The full function looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
file.nativePath += ".db";
if (dbData.db.length() == 0) {
connection.open(file);
statement.sqlConnection = connection;
statement.text = "CREATE TABLE IF NOT EXISTS main.test (id INTEGER PRIMARY KEY AUTOINCREMENT)";
statement.execute();
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
dbData[0].appendChild(newDB);
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
statement.sqlConnection = connection;
statement.text = "CREATE TABLE IF NOT EXISTS db" + newnum.toString() + ".test (id INTEGER PRIMARY KEY AUTOINCREMENT)";
statement.execute();
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
dbData[0].appendChild(newDB);
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
You can see that the database files are saved automatically. You can also see that their size changes (to around 3kb) after we insert a table there.&lt;br /&gt;
&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key" itemClick="menuSelect(event);" /&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem id="newdb" label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="Add" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="dbData"&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="tableData"&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.data.SQLConnection;
import flash.data.SQLStatement;
import flash.events.Event;
import flash.filesystem.File;
import mx.controls.Alert;
import mx.events.FlexNativeMenuEvent;

private var connection:SQLConnection = new SQLConnection();

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; tableData.length; i++) {
tableData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}

private function menuSelect(evt:FlexNativeMenuEvent):void {
(evt.item.@id == "newdb")?(newDatabase()):(void);
}

private function newDatabase():void {
var file:File = File.desktopDirectory.resolvePath("Untitled");
file.addEventListener(Event.SELECT, newSelect);
file.browseForSave("Choose where to save the database");
var newDB:XML;
var statement:SQLStatement = new SQLStatement();
function newSelect(evt:Event):void {
file.nativePath += ".db";
if (dbData.db.length() == 0) {
connection.open(file);
statement.sqlConnection = connection;
statement.text = "CREATE TABLE IF NOT EXISTS main.test (id INTEGER PRIMARY KEY AUTOINCREMENT)";
statement.execute();
dbData = new XMLList(&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;);
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = 1;
newDB.@isBranch = true;
dbData[0].appendChild(newDB);
}else
if (dbData.db.length() &amp;gt; 0) {
var newnum:int = dbData.db.length() + 1;
connection.attach("db"+newnum.toString(), file);
statement.sqlConnection = connection;
statement.text = "CREATE TABLE IF NOT EXISTS db" + newnum.toString() + ".test (id INTEGER PRIMARY KEY AUTOINCREMENT)";
statement.execute();
newDB = &amp;lt;db/&amp;gt;
newDB.@label = file.name;
newDB.@numid = newnum.toString();
newDB.@isBranch = true;
dbData[0].appendChild(newDB);
}
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{dbData}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" /&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{tableData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" editable="false" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-8064350059582633894?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/NxnX3z2V03g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/8064350059582633894/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_04.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/8064350059582633894?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/8064350059582633894?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/NxnX3z2V03g/kirsqlite-flex-air-database-manager_04.html" title="KirSQLite - Flex AIR Database Manager: Part 3" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_04.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMER3wyeip7ImA9WhVVEE8.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-4836686254962587660</id><published>2012-05-03T10:00:00.000+03:00</published><updated>2012-05-03T10:00:06.292+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-03T10:00:06.292+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 2</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ry6vTVUdgOzTsRs1-YceU0Z8Re0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ry6vTVUdgOzTsRs1-YceU0Z8Re0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ry6vTVUdgOzTsRs1-YceU0Z8Re0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ry6vTVUdgOzTsRs1-YceU0Z8Re0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we'll work a little more on the layout - the data grid of the table view screen, specifically.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Firstly, we'll turn the contents of the first NavigatorContent object into a VGroup, which contains an HBox (something I added just now) and an AdvancedDataGrid that we already have.&lt;br /&gt;
&lt;br /&gt;
The HBox is a container which will contain some tools for editing the data grid. We'll add a new column to the datagrid today, which will be a column of CheckBox objects. This way the user will be able to select one or multiple items to perform an action on all of them, for example, delete them all.&lt;br /&gt;
&lt;br /&gt;
For this feature specifically the HBox will contain a CheckBox which will allow the user to select or deselect all of the entries in the table. Set its label to "Select all" and set its change event listener to a function called selectAllChange(), and pass "event" in the parameter. Next to the checkbox, add a button (which does nothing right now) with a label "Delete selected".&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" /&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In the AdvancedDataGrid itself, we'll do quite a few changes too. First, set its id to tableGrid. Set its editable property to true.&lt;br /&gt;
&lt;br /&gt;
In the columns array, add a new AdvancedDataGridColumn object with blank headerText and width of 30. Set its sortable, draggable, editable and resizable properties to false. Set this particular column's itemRenderer to an fx:Component container, which contains an mx:Box container, which contains a CheckBox object. Set this CheckBox's selected property bound to data.sel.&lt;br /&gt;
&lt;br /&gt;
Set the idnum column's editable property to false. Those are all the changes we made to the data grid:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{testData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" editable="false" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The "sel" property of the "data" object is a boolean variable that is set in the testData array collection. Let's add that to all the items in the array and set the values to false. Let's also increase the number of objects in the array for testing purposes:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;mx:ArrayCollection id="testData"&amp;gt;
&amp;lt;fx:Object idnum="1" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="2" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="3" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="4" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="5" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="6" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="7" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="8" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="9" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="10" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="11" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="12" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="13" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="14" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="15" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="16" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="17" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="18" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now create fx:Script tags to create a function called selectAllChange(). This function checks if the "Select all" checkbox is selected or not. If it is selected, then loop through all items in testData and set their "sel" properties to true. Otherwise, loop through them and set the values to false.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.events.Event;
import mx.controls.Alert;

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; testData.length; i++) {
testData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; testData.length; i++) {
testData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
What's worth noticing is that all List-like components in Flex (such as List, DataGrid and TileList) don't always create a separate item renderer for each item in the data provider. For example, if we have 500 items in the List, it doesn't mean that the List will have 500 item renderers that all display values for their own assigned items. In these situations, the component usually has a scrollbar (or even two) and not all of the items are visible. For example, only 10 items might be visible out of 500 at a time. The List component just displays 10 and changes their values as the user scrolls through the content.&lt;br /&gt;
&lt;br /&gt;
This boosts up the performance a lot, but can also cause some problems. Like in our situation - if we have a lot of items in the datagrid, say, 100, we obviously won't be able to view all of them at the same time, maybe around 15 at a time - the data grid will only display these 15 visible items.&lt;br /&gt;
&lt;br /&gt;
What's the problem then? It's that if we use custom modifications, such as CheckBoxes as item renderers of the columns, they also keep their properties like "selected" even if the item renderer is switched to display another item, which might have a different value.&lt;br /&gt;
&lt;br /&gt;
To prevent this, we must bind the select value to data.sel using the {} braces. Because our check boxes are also editable (the user can tick and untick them), we two-way-bind them using @{data.sel}.&lt;br /&gt;
&lt;br /&gt;
Full code so far:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key"/&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="New" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="testTree"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;treeitem label="Database1"&amp;gt;
&amp;lt;treeitem label="Table1"/&amp;gt;
&amp;lt;treeitem label="Table2"/&amp;gt;
&amp;lt;treeitem label="Table3"/&amp;gt;
&amp;lt;/treeitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="testData"&amp;gt;
&amp;lt;fx:Object idnum="1" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="2" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="3" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="4" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="5" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="6" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="7" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="8" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="9" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="10" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="11" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="12" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="13" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="14" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="15" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;fx:Object idnum="16" sel="false" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="17" sel="false" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="18" sel="false" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.events.Event;
import mx.controls.Alert;

private function selectAllChange(evt:Event):void {
var i:int;
if (evt.currentTarget.selected) {
for (i = 0; i &amp;lt; testData.length; i++) {
testData[i].sel = true;
}
} else
if (!evt.currentTarget.selected) {
for (i = 0; i &amp;lt; testData.length; i++) {
testData[i].sel = false;
}
}
tableGrid.invalidateDisplayList();
tableGrid.invalidateList();
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{testTree}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:HBox width="100%" height="30" paddingLeft="8" paddingTop="6"&amp;gt;
&amp;lt;mx:CheckBox label="Select all" change="selectAllChange(event);" /&amp;gt;
&amp;lt;s:Button label="Delete selected" /&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:AdvancedDataGrid id="tableGrid" width="100%" height="100%" dataProvider="{testData}" editable="true"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn headerText=" " width="30" sortable="false" draggable="false" resizable="false" editable="false"&amp;gt;
&amp;lt;mx:itemRenderer&amp;gt;
&amp;lt;fx:Component&amp;gt;
&amp;lt;mx:Box width="30" horizontalAlign="center"&amp;gt;
&amp;lt;mx:CheckBox selected="@{data.sel}" /&amp;gt;
&amp;lt;/mx:Box&amp;gt;
&amp;lt;/fx:Component&amp;gt;
&amp;lt;/mx:itemRenderer&amp;gt;
&amp;lt;/mx:AdvancedDataGridColumn&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" editable="false" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-4836686254962587660?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/g0_s4ZRL2Oo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/4836686254962587660/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_03.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/4836686254962587660?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/4836686254962587660?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/g0_s4ZRL2Oo/kirsqlite-flex-air-database-manager_03.html" title="KirSQLite - Flex AIR Database Manager: Part 2" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager_03.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEcFRX0zeyp7ImA9WhVWGUk.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-7418444320336930453</id><published>2012-05-02T10:00:00.000+03:00</published><updated>2012-05-02T10:00:14.383+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-02T10:00:14.383+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><category scheme="http://www.blogger.com/atom/ns#" term="SQL" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSQLite step-by-step" /><title>KirSQLite - Flex AIR Database Manager: Part 1</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/t_gkTvzcLPnnvBLeI6zp4qIfdJs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/t_gkTvzcLPnnvBLeI6zp4qIfdJs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/t_gkTvzcLPnnvBLeI6zp4qIfdJs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/t_gkTvzcLPnnvBLeI6zp4qIfdJs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;Today we will start working on a new application - an SQLite database manager.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
The goal is to make a program, which can be used as a tool to create, view, edit, search through databases. The SQL database engine we're using is SQLite.&lt;br /&gt;
&lt;br /&gt;
As usual, I am using FlashDevelop to create the program. I highly recommend you do the same. Remember to update your software to the latest available version. Do the same with Flex and AIR SDK too.&lt;br /&gt;
&lt;br /&gt;
Go to Project &amp;gt; New Project. Create a new Flex 4 AIR Projector, name it KirSQLite.&lt;br /&gt;
&lt;br /&gt;
Now find the Main.mxml file in the newly generated src folder in the project directory, and let's start creating the layout of our application. &lt;br /&gt;
&lt;br /&gt;
We're going to use the native toolbar menu thing, so we'll need to create an XML object in the Declarations tags with the menu contents. Right now, I'll just add 2 main menu items - Database and Table, but as I work on the project, I'll continue adding more items and subitems. Let's just have this for now:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="New" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
You can see that the id of the XML object is windowMenu. We'll use it to declare the native menu using menu tags like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key"/&amp;gt;
&amp;lt;/s:menu&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now let's move below the toolbar. Here I'm going to have the screen divided in two areas - the first one will be a vertical bar, which will contain the databases that are open right now, with all their tables. We're going to use a Tree object for this.&lt;br /&gt;
&lt;br /&gt;
The second area will be a larger one, and will consist of a TabNavigator. Once again, let's add just some basic content for the component so that we can (and probably will) add and change it later. The current NavigatorContent objects of the TabNavigator container will be "Table contents", "Edit columns" and "Query".&lt;br /&gt;
&lt;br /&gt;
Let's add content for the first NavigatorContent for now. This content will be a big AdvancedDataGrid object.&lt;br /&gt;
&lt;br /&gt;
For now, let's add some placeholder data for the AdvancedDataGrid and the Tree objects, to see what the program is going to look like with actual content.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{testTree}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;mx:AdvancedDataGrid width="100%" height="100%" dataProvider="{testData}"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
You can see that I've bound the Tree object's dataProvider to "testTree", and the AdvancedDataGrid one's to "testData". These both objects are placed within the Declarations tags (along with windowMenu), and are simple XMLList and ArrayCollection objects:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="New" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="testTree"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;treeitem label="Database1"&amp;gt;
&amp;lt;treeitem label="Table1"/&amp;gt;
&amp;lt;treeitem label="Table2"/&amp;gt;
&amp;lt;treeitem label="Table3"/&amp;gt;
&amp;lt;/treeitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="testData"&amp;gt;
&amp;lt;fx:Object idnum="1" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="2" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="3" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code so far:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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" showStatusBar="false"&amp;gt;
   
&amp;lt;s:menu&amp;gt;
&amp;lt;mx:FlexNativeMenu dataProvider="{windowMenu}" showRoot="false" labelField="@label" keyEquivalentField="@key"/&amp;gt;
&amp;lt;/s:menu&amp;gt;

&amp;lt;fx:Declarations&amp;gt;
&amp;lt;fx:XML id="windowMenu"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;menuitem label="Database"&amp;gt;
&amp;lt;menuitem label="New" key="n" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Open" key="o" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save" key="s" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Save As.." key="s" controlKey="true" shiftKey="true" /&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;menuitem label="Table"&amp;gt;
&amp;lt;menuitem label="New" key="t" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Edit" key="e" controlKey="true" /&amp;gt;
&amp;lt;menuitem label="Delete"/&amp;gt;
&amp;lt;/menuitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XML&amp;gt;
&amp;lt;fx:XMLList id="testTree"&amp;gt;
&amp;lt;root&amp;gt;
&amp;lt;treeitem label="Database1"&amp;gt;
&amp;lt;treeitem label="Table1"/&amp;gt;
&amp;lt;treeitem label="Table2"/&amp;gt;
&amp;lt;treeitem label="Table3"/&amp;gt;
&amp;lt;/treeitem&amp;gt;
&amp;lt;/root&amp;gt;
&amp;lt;/fx:XMLList&amp;gt;
&amp;lt;mx:ArrayCollection id="testData"&amp;gt;
&amp;lt;fx:Object idnum="1" firstname="John" lastname="Jackson" age="32" /&amp;gt;
&amp;lt;fx:Object idnum="2" firstname="Ted" lastname="Jacobson" age="25" /&amp;gt;
&amp;lt;fx:Object idnum="3" firstname="Bob" lastname="Smith" age="33" /&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;s:HGroup gap="0" width="100%" height="100%"&amp;gt;
&amp;lt;s:VGroup width="200" height="100%" gap="0"&amp;gt;
&amp;lt;mx:Tree width="100%" height="100%" dataProvider="{testTree}" showRoot="false" labelField="@label"/&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" gap="0"&amp;gt;
&amp;lt;mx:TabNavigator width="100%" height="100%" paddingTop="0"&amp;gt;
&amp;lt;s:NavigatorContent label="Table contents"&amp;gt;
&amp;lt;mx:AdvancedDataGrid width="100%" height="100%" dataProvider="{testData}"&amp;gt;
&amp;lt;mx:columns&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="idnum" headerText="ID" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="firstname" headerText="First name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="lastname" headerText="Last name" /&amp;gt;
&amp;lt;mx:AdvancedDataGridColumn dataField="age" headerText="Age"/&amp;gt;
&amp;lt;/mx:columns&amp;gt;
&amp;lt;/mx:AdvancedDataGrid&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Edit columns"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent label="Query"&amp;gt;

&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:TabNavigator&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We'll continue in the next part!&lt;br /&gt;
&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-7418444320336930453?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/9G3hy378I3c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/7418444320336930453/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/7418444320336930453?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/7418444320336930453?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/9G3hy378I3c/kirsqlite-flex-air-database-manager.html" title="KirSQLite - Flex AIR Database Manager: Part 1" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsqlite-flex-air-database-manager.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0EFQnYyeyp7ImA9WhVWGEg.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-6197592388170691381</id><published>2012-05-01T10:00:00.000+03:00</published><updated>2012-05-01T10:00:13.893+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-01T10:00:13.893+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSizer step-by-step" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><title>KirSizer - Flex AIR Image Sizer - DOWNLOAD</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/j1kZeCOhW9aMUUFuKMeGkAhWpg8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/j1kZeCOhW9aMUUFuKMeGkAhWpg8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/j1kZeCOhW9aMUUFuKMeGkAhWpg8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/j1kZeCOhW9aMUUFuKMeGkAhWpg8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;Download KirSizer, a tool for resizing, renaming and compressing multiple pictures in one go.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-SQOl2dordeY/T4sypI-hTDI/AAAAAAAAAgU/dtuGJiPoyTo/s1600/logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-SQOl2dordeY/T4sypI-hTDI/AAAAAAAAAgU/dtuGJiPoyTo/s1600/logo.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Download mirrors&lt;/b&gt;: &lt;a href="http://dl.dropbox.com/u/63022573/Work/KirSizerFree.rar"&gt;Dropbox&lt;/a&gt;, &lt;a href="http://www.filefactory.com/file/4s4qc27cxwav/n/KirSizerFree_rar"&gt;FileFactory&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
This is the free version of the program with limited functionality. For more options, efficiency and features, please consider purchasing the full verison (it will be available soon!)&lt;br /&gt;
&lt;br /&gt;
Hope you enjoy the program and find it useful!&lt;br /&gt;
&lt;br /&gt;
I made a detailed step-by-step tutorial on making the free version of this program from scratch (14 parts in total). &lt;a href="http://www.blogger.com/blogger.g?blogID=2631617199049210138#"&gt;&lt;b&gt;Go to the first part.&lt;/b&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-6197592388170691381?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/8aMC0jMl4Qc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/6197592388170691381/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/05/kirsizer-flex-air-image-sizer-download.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6197592388170691381?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6197592388170691381?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/8aMC0jMl4Qc/kirsizer-flex-air-image-sizer-download.html" title="KirSizer - Flex AIR Image Sizer - DOWNLOAD" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-SQOl2dordeY/T4sypI-hTDI/AAAAAAAAAgU/dtuGJiPoyTo/s72-c/logo.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/05/kirsizer-flex-air-image-sizer-download.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UESHw7cSp7ImA9WhVWF0s.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-3885563915767858000</id><published>2012-04-30T10:00:00.000+03:00</published><updated>2012-04-30T10:00:09.209+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-30T10:00:09.209+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSizer step-by-step" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><title>KirSizer - Flex AIR Image Sizer app: Part 14</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UqHRB2MMoDU12mfadFaNdhAFIuc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UqHRB2MMoDU12mfadFaNdhAFIuc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UqHRB2MMoDU12mfadFaNdhAFIuc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UqHRB2MMoDU12mfadFaNdhAFIuc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will make the log text area refresh when it reaches bottom and add an option to skip files if their size is already correct.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
When I was testing KirSizer on a big amount of images, I noticed that quite a lot of images already had the correct size and thought that it would take less time if I just checked if the picture's size are already correct and skip them. However, just skipping all files for just having correct sizes is wrong, since KirSizer is not only capable of resizing images, but also renaming them, changing their format and also compress pictures. That is why I decided to include the "Skip" feature as a toggleable option.&lt;br /&gt;
&lt;br /&gt;
I also noticed that the log text can get quite big. To prevent any problems with too large text, I decided to clear the text area every time it reaches 24 lines. This way it is still informative, but takes much less space and perhaps this way it even is more comprehendable.&lt;br /&gt;
&lt;br /&gt;
First we'll do the text limitation feature. Create a function called checkLineLimit() which checks if the number of lines exceeds 24 and set logArea's text to blank if so.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function checkLineLimit():void {
if (countLines(logArea.text) &amp;gt; 24) {
logArea.text = "";
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The countLines() function that I used returns an integer value, which equals to the number of elements of the array that's created by splitting the text area's text by new line characters:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function countLines(text:String):int {
var splittedText:Array = text.split("\n");
var lines:int = splittedText.length;
return lines;
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
We call this function in the beginning of nextAction(), in its first if statement:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
checkLineLimit();
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
appendHighlight("ERROR: incorrect amount of image files found in folder " + folder.name + "\n", 0xff2222);
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
appendHighlight("ERROR: folder not found (" + folder.name + ")\n", 0xff2222);
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
appendHighlight("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles, 0xffff44);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
statusText.setStyle("color", 0x44ff44);
statusText.text = "COMPLETED";
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now let's work on the skipping feature. Go to the options screen's NavigatorContent object and create a new CheckBox object with an id skipCorrect and selected property set to false:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" /&amp;gt;

&amp;lt;s:Label&amp;gt;Resize options:&amp;lt;/s:Label&amp;gt;

&amp;lt;mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Width:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" /&amp;gt;
&amp;lt;mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="1" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Height:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/&amp;gt;
&amp;lt;mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="1" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:CheckBox id="skipCorrect" label="Skip file if the size is correct" selected="false" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output file names:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:TextInput id="nameInput" width="240" text="%initialName%" /&amp;gt;
&amp;lt;s:Button width="35" label="?" click="contentStack.selectedIndex = 2;" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label&amp;gt;Output destination:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" /&amp;gt;
&amp;lt;s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle" width="100%"&amp;gt;
&amp;lt;s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" /&amp;gt;
&amp;lt;s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output format:&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output JPG quality:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Button label="Resize" width="100%" click="beginResize();" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now we're going to add some major changes to the resizeNext() function.&lt;br /&gt;
&lt;br /&gt;
However, the code that we add will not be very big. We're just going to add a needResize boolean value which is set to false if the new height and width equal the old height and width of the image. We are, however, going to have to move large piece of code from the onTimer() function into the resizeNext() function itself. &lt;br /&gt;
&lt;br /&gt;
We are doing this so that we can calculate new size of the picture before starting the timer and wasting time on it. We just load the image, then set all the variables, such as newExtension, currentW, currentH, newW, newH, ratio, newName and needResize, calculate their values, check if the size has changed, and if it hasn't - don't even bother with the timer and just call nextAction(). Otherwise, start the timer and resize normally.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) {
currentNum++;
}
}

if(file!=null) logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (file != null &amp;&amp; !file.exists) {
canProceed = false;
appendHighlight("ERROR: File not found\n", 0xff2222);
errorText += "File not found at location: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}


// Error: bad extension
if (file != null &amp;&amp; (file.exists &amp;&amp; file.extension.toLowerCase() != "png" &amp;&amp; file.extension.toLowerCase() != "jpg" &amp;&amp; file.extension.toLowerCase() != "jpeg")) {
canProceed = false;
appendHighlight("ERROR: Incorrect extension\n", 0xff2222);
errorText += "Incorrect file extension: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}

if (file == null) { 
canProceed = false
nextAction();
};

var newExtension:String;
var currentW:int;
var currentH:int;
var newW:int;
var newH:int;
var ratio:Number;
var newName:String

var needResize:Boolean = true;

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
// new name
newName = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
currentW = loader.content.width;
currentH = loader.content.height;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH &amp;gt; newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

if (skipCorrect.selected &amp;&amp; currentW == newW &amp;&amp; currentH == newH) {
needResize = false;
logArea.appendText("Skipped: " + file.name + "\n");
nextAction();
}

if(needResize){
var timer:Timer = new Timer(200, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}
}

function onTimer(evt:TimerEvent):void {
// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();
successFiles++;

nextAction();
}

}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer" creationComplete="init();"&amp;gt;
   
&amp;lt;fx:Declarations&amp;gt;
&amp;lt;mx:ArrayCollection id="measures"&amp;gt;
&amp;lt;fx:String&amp;gt;%&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;px&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="actions"&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, proportional height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional sizes to fit specified sizes&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="formats"&amp;gt;
&amp;lt;fx:String&amp;gt;Same format as initial file&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to JPG&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to PNG&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="300"/&amp;gt;
&amp;lt;mx:Fade id="fadeOut" alphaFrom="1" alphaTo="0" duration="300"/&amp;gt;
&amp;lt;mx:TitleWindow id="waitWindow" title="Please wait" width="240" height="70" showCloseButton="false"&amp;gt;
&amp;lt;s:Group width="100%" height="100%"&amp;gt;
&amp;lt;s:Label top="10" left="10" id="waitLabel" width="220" color="0x000000" /&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Style&amp;gt;
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

#contentStack{
backgroundColor: #313131;
}

s|Label{
color: #fcfcfc;
}

s|Button{
chromeColor: #636363;
}

mx|ComboBox{
chromeColor: #636363;
color: #fcfcfc;
contentBackgroundColor: #000000;
rollOverColor: #aaaaaa;
selectionColor: #ffffff;
}

#logArea{
contentBackgroundColor: #111111;
focusedTextSelectionColor: #0000ff;
fontFamily: "Courier New";
color: #aaaaaa;
}
&amp;lt;/fx:Style&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.FileListEvent;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.system.ImageDecodingPolicy;
import flash.text.TextFormat;
import flash.utils.ByteArray;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.controls.Image;
import mx.effects.easing.Linear;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
import mx.events.StateChangeEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import mx.managers.PopUpManager;
import flashx.textLayout.formats.TextLayoutFormat
[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);
private var totalFiles:int;
private var currentNum:int;
private var totalErrors:int;
private var folderArray:Array = [];
private var destinationPicked:String = "";
private var errorText:String;
private var successFiles:int;

private function init():void {
addEventListener(KeyboardEvent.KEY_DOWN, keybDown);
}

private function keybDown(evt:KeyboardEvent):void {
if (evt.ctrlKey &amp;&amp; evt.keyCode == 65) {
var arr:Array = [];
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
arr.push(i);
}
tileList.selectedIndices = arr;
}
}

private function actionChange():void{
switch (actionCombo.selectedIndex) {
case 0:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 1:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = false;
heightMeasure.enabled = false;
break;
case 2:
newWidth.enabled = false;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 3:
newWidth.enabled = true;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = false;
widthMeasure.selectedIndex = 1;
heightMeasure.selectedIndex = 1;
}
}

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

private function filesSelected(evt:FileListEvent):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting files...";
var timer:Timer = new Timer(500, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFiles(evt.files);
PopUpManager.removePopUp(waitWindow);
}
}

private function addFolder():void {
var file:File = new File();
file.browseForDirectory("Select a directory");
file.addEventListener(Event.SELECT, folderSelected);
}

private function folderSelected(evt:Event):void {
var file:File = evt.currentTarget as File;
Alert.show("Do you want to select subfolders too?", "Recursive selection?", Alert.YES | Alert.NO, null, warningClose);

function warningClose(ev:CloseEvent):void {
if (ev.detail == Alert.YES) {
startFolder(file, true);
}
if (ev.detail == Alert.NO) {
startFolder(file, false);
}
}
}

private function startFolder(file:File, recurs:Boolean):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting folders...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFolder(file, recurs);
PopUpManager.removePopUp(waitWindow);
}
}

private function doFiles(files:Array):void {
for (var i:int = 0; i &amp;lt; files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u &amp;lt; selectedFiles.length; u++) {
if (selectedFiles[u].type == 0 &amp;&amp; selectedFiles[u].path == files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem({type:0, path:files[i].nativePath});
}
updateTotalFiles();
}

private function doFolder(file:File, isRecursive:Boolean):void {
var picturesInFolder:int = 0;
var childFiles:Array = file.getDirectoryListing();
for (var i:int = 0; i &amp;lt; childFiles.length; i++) {
if (childFiles[i].extension == "png" || childFiles[i].extension == "jpg" || childFiles[i].extension == "jpeg") {
picturesInFolder++;
}
if (childFiles[i].isDirectory &amp;&amp; isRecursive) {
doFolder(childFiles[i], true);
}
}
if (picturesInFolder &amp;gt; 0) {
var alreadySelected:Boolean = false;
for (var a:int = 0; a &amp;lt; selectedFiles.length; a++) {
if (selectedFiles[a].type == 1 &amp;&amp; selectedFiles[a].path == file.nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem( { type:1, path:file.nativePath, name:file.name, num:picturesInFolder } );
updateTotalFiles();
}
}

private function updateTotalFiles():void {
totalFiles = 0;
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
if (selectedFiles[i].type==1) {
totalFiles += selectedFiles[i].num;
}else {
totalFiles++;
}
}
labelSelected.text = totalFiles + " files selected";
}

private function removeSelected():void {
while (tileList.selectedItems.length &amp;gt; 0) {
selectedFiles.removeItemAt(tileList.selectedIndices[0]);
}
updateTotalFiles();
}

private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') &amp;lt; 0 &amp;&amp; nameInput.text.indexOf('%num%') &amp;lt; 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected &amp;&amp; destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')&amp;gt;=0 || testName.indexOf("\\")&amp;gt;=0 || testName.indexOf('?')&amp;gt;=0 || testName.indexOf('%')&amp;gt;=0 ||
testName.indexOf('*')&amp;gt;=0 || testName.indexOf(':')&amp;gt;=0 || testName.indexOf('|')&amp;gt;=0 || testName.indexOf('"')&amp;gt;=0 ||
testName.indexOf('&amp;lt;') &amp;gt;= 0 || testName.indexOf('&amp;gt;') &amp;gt;= 0 || testName.indexOf('.') &amp;gt;= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" &amp;lt; &amp;gt; . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
statusText.setStyle("color", 0xffff55);
statusText.text = "IN PROGRESS";
nextAction();
}
}
}

private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) {
currentNum++;
}
}

if(file!=null) logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (file != null &amp;&amp; !file.exists) {
canProceed = false;
appendHighlight("ERROR: File not found\n", 0xff2222);
errorText += "File not found at location: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}


// Error: bad extension
if (file != null &amp;&amp; (file.exists &amp;&amp; file.extension.toLowerCase() != "png" &amp;&amp; file.extension.toLowerCase() != "jpg" &amp;&amp; file.extension.toLowerCase() != "jpeg")) {
canProceed = false;
appendHighlight("ERROR: Incorrect extension\n", 0xff2222);
errorText += "Incorrect file extension: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}

if (file == null) { 
canProceed = false
nextAction();
};

var newExtension:String;
var currentW:int;
var currentH:int;
var newW:int;
var newH:int;
var ratio:Number;
var newName:String

var needResize:Boolean = true;

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
// new name
newName = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
currentW = loader.content.width;
currentH = loader.content.height;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH &amp;gt; newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

if (skipCorrect.selected &amp;&amp; currentW == newW &amp;&amp; currentH == newH) {
needResize = false;
logArea.appendText("Skipped: " + file.name + "\n");
nextAction();
}

if(needResize){
var timer:Timer = new Timer(200, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}
}

function onTimer(evt:TimerEvent):void {
// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();
successFiles++;

nextAction();
}

}

private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
checkLineLimit();
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
appendHighlight("ERROR: incorrect amount of image files found in folder " + folder.name + "\n", 0xff2222);
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
appendHighlight("ERROR: folder not found (" + folder.name + ")\n", 0xff2222);
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
appendHighlight("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles, 0xffff44);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
statusText.setStyle("color", 0x44ff44);
statusText.text = "COMPLETED";
}
}

private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}

private function saveErrors():void {
var file:File = File.desktopDirectory.resolvePath("errors.txt");
file.browseForSave("Save error log");
file.addEventListener(Event.SELECT, errorSelect);

function errorSelect(evt:Event):void {
if (!file.nativePath.match(/^.*\.(txt)$/i)) {
file.nativePath += ".txt";
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeUTFBytes(errorText);
fileStream.close();
}
}

private function appendHighlight(text:String, color:uint):void {
var formatHighlight:TextLayoutFormat = new TextLayoutFormat();
formatHighlight.color = color;
var formatNormal:TextLayoutFormat = new TextLayoutFormat();
formatNormal.color = 0xaaaaaa;
var firstAnchor:int = logArea.text.length;
logArea.appendText(text);
var secondAnchor:int = logArea.text.length;
logArea.setFormatOfRange(formatHighlight, firstAnchor, secondAnchor);
logArea.setFormatOfRange(formatNormal, secondAnchor, secondAnchor);
}

private function checkLineLimit():void {
if (countLines(logArea.text) &amp;gt; 24) {
logArea.text = "";
}
}

private function countLines(text:String):int {
var splittedText:Array = text.split("\n");
var lines:int = splittedText.length;
return lines;
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;
   
&amp;lt;mx:ViewStack id="contentStack" width="100%" height="100%" &amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Label id="labelSelected"&amp;gt;0 files selected&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" allowMultipleSelection="true" selectionColor="0xff0000" rollOverColor="0xff8888" /&amp;gt;
&amp;lt;s:Button label="Add folder" width="100%" click="addFolder();" /&amp;gt;
&amp;lt;s:Button label="Add files" width="100%" click="addFiles();" /&amp;gt;
&amp;lt;s:Button label="{'Remove ' + tileList.selectedItems.length + ' items'}" width="100%" enabled="{tileList.selectedItems.length&amp;gt;0}" click="removeSelected();" /&amp;gt;
&amp;lt;s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" /&amp;gt;

&amp;lt;s:Label&amp;gt;Resize options:&amp;lt;/s:Label&amp;gt;

&amp;lt;mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Width:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" /&amp;gt;
&amp;lt;mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="1" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Height:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/&amp;gt;
&amp;lt;mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="1" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:CheckBox id="skipCorrect" label="Skip file if the size is correct" selected="false" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output file names:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:TextInput id="nameInput" width="240" text="%initialName%" /&amp;gt;
&amp;lt;s:Button width="35" label="?" click="contentStack.selectedIndex = 2;" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label&amp;gt;Output destination:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" /&amp;gt;
&amp;lt;s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle" width="100%"&amp;gt;
&amp;lt;s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" /&amp;gt;
&amp;lt;s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output format:&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output JPG quality:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Button label="Resize" width="100%" click="beginResize();" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" top="10" left="10"&amp;gt;
&amp;lt;s:VGroup width="100%" height="410" gap="20"&amp;gt;
&amp;lt;s:Label fontSize="20" width="100%" fontWeight="bold"&amp;gt;Output file names&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;You can build names for output files using provided wildcards and combining them with text.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;For example, "%initialName%_new" will generate names like "pic_new.jpg", "img_new.png", etc.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Available wildcards are:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%initialName%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Puts the initial name of the file that's being resized.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%num%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Gives each file a unique id number starting from 1.&amp;lt;/s:Label&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:Button label="Back" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" height="440" top="10" left="10"&amp;gt;
&amp;lt;s:Label width="100%" color="0xff3333" fontWeight="bold"&amp;gt;Do not add, remove or rename selected files.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Group width="100%"&amp;gt;
&amp;lt;mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1 / %2" color="0xffffff" /&amp;gt;
&amp;lt;s:Label top="20" left="200" textAlign="right" id="statusText" fontWeight="bold"/&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;s:TextArea id="logArea" editable="false" width="100%" height="100%" /&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="buttonError" label="Save error log" click="saveErrors();" width="136" /&amp;gt;
&amp;lt;s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:ViewStack&amp;gt;
&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-3885563915767858000?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/khZ9E4l-2Qw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/3885563915767858000/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_30.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/3885563915767858000?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/3885563915767858000?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/khZ9E4l-2Qw/kirsizer-flex-air-image-sizer-app-part_30.html" title="KirSizer - Flex AIR Image Sizer app: Part 14" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_30.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUDRH84fCp7ImA9WhVWFkU.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-2922092656149947975</id><published>2012-04-29T10:00:00.000+03:00</published><updated>2012-04-29T11:14:35.134+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-29T11:14:35.134+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSizer step-by-step" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><title>KirSizer - Flex AIR Image Sizer app: Part 13</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/BCZl_nj6uATCkHc0wJ8a9CefSnA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/BCZl_nj6uATCkHc0wJ8a9CefSnA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/BCZl_nj6uATCkHc0wJ8a9CefSnA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/BCZl_nj6uATCkHc0wJ8a9CefSnA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will add color highlighting to the log area, as well as a colorful indicator of the operation's progress.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
We'll use colors in 2 situations in the log area. The first one is to highlight errors - we'll use red color for that. The second time we will use highlighting is in the last 3 lines - the results of the operation. We will use yellow color for that.&lt;br /&gt;
&lt;br /&gt;
To highlight a text fragment in a text area, we'll use the setFormatOfRange() method. We'll need to apply a TextLayoutFormat object, and we'll also need 2 integer numbers - the position of the beginning and ending anchors for the text that needs to be highlighted.&lt;br /&gt;
&lt;br /&gt;
We'll create a new function for that, call it appendHighlight.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function appendHighlight(text:String, color:uint):void {
var formatHighlight:TextLayoutFormat = new TextLayoutFormat();
formatHighlight.color = color;
var formatNormal:TextLayoutFormat = new TextLayoutFormat();
formatNormal.color = 0xaaaaaa;
var firstAnchor:int = logArea.text.length;
logArea.appendText(text);
var secondAnchor:int = logArea.text.length;
logArea.setFormatOfRange(formatHighlight, firstAnchor, secondAnchor);
logArea.setFormatOfRange(formatNormal, secondAnchor, secondAnchor);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
As you can see, we pass a text value to the function, and the function adds the text to the logArea, while also highlighting it, using the color that we pass as the parameter. After changing the format of the error message, we revert the format to the normal one.&lt;br /&gt;
&lt;br /&gt;
Now, we need to change every line that uses the logArea's appendText() method to display an Error message, or to print the results of the operation and change logArea.appendText to appendHighlight, while also passing the second color value parameter. As I said, we need to do this in every place that displays an error message or the results. I'll include full code below if you have trouble finding them.&lt;br /&gt;
&lt;br /&gt;
Next, let's make a colorful text indicator that is located under the progress bar and turns yellow when the images are being resized and says "IN PROGRESS", and turns green when the operation is completed and asys "COMPLETED".&lt;br /&gt;
&lt;br /&gt;
Go to the last NavigatorContent object and turn the progress bar into a group, then add a new Label object inside. Set its top and left properties so that it is positioned under the progress bar. Set its id to statusText.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" height="440" top="10" left="10"&amp;gt;
&amp;lt;s:Label width="100%" color="0xff3333" fontWeight="bold"&amp;gt;Do not add, remove or rename selected files.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Group width="100%"&amp;gt;
&amp;lt;mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" /&amp;gt;
&amp;lt;s:Label top="20" left="200" textAlign="right" id="statusText" fontWeight="bold"/&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;s:TextArea id="logArea" editable="false" width="100%" height="100%" /&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="buttonError" label="Save error log" click="saveErrors();" width="136" /&amp;gt;
&amp;lt;s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In beginResize() function, in the last if statement add 2 lines that set the text and color of the statusText object using "text" property and setStyle() method:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
statusText.setStyle("color", 0xffff55);
statusText.text = "IN PROGRESS";
nextAction();
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In nextAction(), when the operation is completed, change the text's color to green and value to "COMPLETED":&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
appendHighlight("ERROR: incorrect amount of image files found in folder " + folder.name + "\n", 0xff2222);
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
appendHighlight("ERROR: folder not found (" + folder.name + ")\n", 0xff2222);
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
appendHighlight("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles, 0xffff44);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
statusText.setStyle("color", 0x44ff44);
statusText.text = "COMPLETED";
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer" creationComplete="init();"&amp;gt;
   
&amp;lt;fx:Declarations&amp;gt;
&amp;lt;mx:ArrayCollection id="measures"&amp;gt;
&amp;lt;fx:String&amp;gt;%&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;px&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="actions"&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, proportional height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional sizes to fit specified sizes&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="formats"&amp;gt;
&amp;lt;fx:String&amp;gt;Same format as initial file&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to JPG&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to PNG&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="300"/&amp;gt;
&amp;lt;mx:Fade id="fadeOut" alphaFrom="1" alphaTo="0" duration="300"/&amp;gt;
&amp;lt;mx:TitleWindow id="waitWindow" title="Please wait" width="240" height="70" showCloseButton="false"&amp;gt;
&amp;lt;s:Group width="100%" height="100%"&amp;gt;
&amp;lt;s:Label top="10" left="10" id="waitLabel" width="220" color="0x000000" /&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Style&amp;gt;
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

#contentStack{
backgroundColor: #313131;
}

s|Label{
color: #fcfcfc;
}

s|Button{
chromeColor: #636363;
}

mx|ComboBox{
chromeColor: #636363;
color: #fcfcfc;
contentBackgroundColor: #000000;
rollOverColor: #aaaaaa;
selectionColor: #ffffff;
}

#logArea{
contentBackgroundColor: #111111;
focusedTextSelectionColor: #0000ff;
fontFamily: "Courier New";
color: #aaaaaa;
}
&amp;lt;/fx:Style&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.FileListEvent;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.system.ImageDecodingPolicy;
import flash.text.TextFormat;
import flash.utils.ByteArray;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.controls.Image;
import mx.effects.easing.Linear;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
import mx.events.StateChangeEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import mx.managers.PopUpManager;
import flashx.textLayout.formats.TextLayoutFormat
[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);
private var totalFiles:int;
private var currentNum:int;
private var totalErrors:int;
private var folderArray:Array = [];
private var destinationPicked:String = "";
private var errorText:String;
private var successFiles:int;

private function init():void {
addEventListener(KeyboardEvent.KEY_DOWN, keybDown);
}

private function keybDown(evt:KeyboardEvent):void {
if (evt.ctrlKey &amp;&amp; evt.keyCode == 65) {
var arr:Array = [];
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
arr.push(i);
}
tileList.selectedIndices = arr;
}
}

private function actionChange():void{
switch (actionCombo.selectedIndex) {
case 0:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 1:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = false;
heightMeasure.enabled = false;
break;
case 2:
newWidth.enabled = false;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 3:
newWidth.enabled = true;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = false;
widthMeasure.selectedIndex = 1;
heightMeasure.selectedIndex = 1;
}
}

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

private function filesSelected(evt:FileListEvent):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting files...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFiles(evt.files);
PopUpManager.removePopUp(waitWindow);
}
}

private function addFolder():void {
var file:File = new File();
file.browseForDirectory("Select a directory");
file.addEventListener(Event.SELECT, folderSelected);
}

private function folderSelected(evt:Event):void {
var file:File = evt.currentTarget as File;
Alert.show("Do you want to select subfolders too?", "Recursive selection?", Alert.YES | Alert.NO, null, warningClose);

function warningClose(ev:CloseEvent):void {
if (ev.detail == Alert.YES) {
startFolder(file, true);
}
if (ev.detail == Alert.NO) {
startFolder(file, false);
}
}
}

private function startFolder(file:File, recurs:Boolean):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting folders...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFolder(file, recurs);
PopUpManager.removePopUp(waitWindow);
}
}

private function doFiles(files:Array):void {
for (var i:int = 0; i &amp;lt; files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u &amp;lt; selectedFiles.length; u++) {
if (selectedFiles[u].type == 0 &amp;&amp; selectedFiles[u].path == files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem({type:0, path:files[i].nativePath});
}
updateTotalFiles();
}

private function doFolder(file:File, isRecursive:Boolean):void {
var picturesInFolder:int = 0;
var childFiles:Array = file.getDirectoryListing();
for (var i:int = 0; i &amp;lt; childFiles.length; i++) {
if (childFiles[i].extension == "png" || childFiles[i].extension == "jpg" || childFiles[i].extension == "jpeg") {
picturesInFolder++;
}
if (childFiles[i].isDirectory &amp;&amp; isRecursive) {
doFolder(childFiles[i], true);
}
}
if (picturesInFolder &amp;gt; 0) {
var alreadySelected:Boolean = false;
for (var a:int = 0; a &amp;lt; selectedFiles.length; a++) {
if (selectedFiles[a].type == 1 &amp;&amp; selectedFiles[a].path == file.nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem( { type:1, path:file.nativePath, name:file.name, num:picturesInFolder } );
updateTotalFiles();
}
}

private function updateTotalFiles():void {
totalFiles = 0;
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
if (selectedFiles[i].type==1) {
totalFiles += selectedFiles[i].num;
}else {
totalFiles++;
}
}
labelSelected.text = totalFiles + " files selected";
}

private function removeSelected():void {
while (tileList.selectedItems.length &amp;gt; 0) {
selectedFiles.removeItemAt(tileList.selectedIndices[0]);
}
updateTotalFiles();
}

private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') &amp;lt; 0 &amp;&amp; nameInput.text.indexOf('%num%') &amp;lt; 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected &amp;&amp; destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')&amp;gt;=0 || testName.indexOf("\\")&amp;gt;=0 || testName.indexOf('?')&amp;gt;=0 || testName.indexOf('%')&amp;gt;=0 ||
testName.indexOf('*')&amp;gt;=0 || testName.indexOf(':')&amp;gt;=0 || testName.indexOf('|')&amp;gt;=0 || testName.indexOf('"')&amp;gt;=0 ||
testName.indexOf('&amp;lt;') &amp;gt;= 0 || testName.indexOf('&amp;gt;') &amp;gt;= 0 || testName.indexOf('.') &amp;gt;= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" &amp;lt; &amp;gt; . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
statusText.setStyle("color", 0xffff55);
statusText.text = "IN PROGRESS";
nextAction();
}
}
}

private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) {
currentNum++;
}
}

if(file!=null) logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (file != null &amp;&amp; !file.exists) {
canProceed = false;
appendHighlight("ERROR: File not found\n", 0xff2222);
errorText += "File not found at location: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}


// Error: bad extension
if (file != null &amp;&amp; (file.exists &amp;&amp; file.extension.toLowerCase() != "png" &amp;&amp; file.extension.toLowerCase() != "jpg" &amp;&amp; file.extension.toLowerCase() != "jpeg")) {
canProceed = false;
appendHighlight("ERROR: Incorrect extension\n", 0xff2222);
errorText += "Incorrect file extension: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}

if (file == null) { 
canProceed = false
nextAction();
};

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
var timer:Timer = new Timer(500, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}

function onTimer(evt:TimerEvent):void {
// new name
var newName:String = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
var newExtension:String;
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
var currentW:int = loader.content.width;
var currentH:int = loader.content.height;
var newW:int;
var newH:int;
var ratio:Number;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH &amp;gt; newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();
successFiles++;

nextAction();
}

}

private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
appendHighlight("ERROR: incorrect amount of image files found in folder " + folder.name + "\n", 0xff2222);
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
appendHighlight("ERROR: folder not found (" + folder.name + ")\n", 0xff2222);
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
appendHighlight("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles, 0xffff44);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
statusText.setStyle("color", 0x44ff44);
statusText.text = "COMPLETED";
}
}

private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}

private function saveErrors():void {
var file:File = File.desktopDirectory.resolvePath("errors.txt");
file.browseForSave("Save error log");
file.addEventListener(Event.SELECT, errorSelect);

function errorSelect(evt:Event):void {
if (!file.nativePath.match(/^.*\.(txt)$/i)) {
file.nativePath += ".txt";
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeUTFBytes(errorText);
fileStream.close();
}
}

private function appendHighlight(text:String, color:uint):void {
var formatHighlight:TextLayoutFormat = new TextLayoutFormat();
formatHighlight.color = color;
var formatNormal:TextLayoutFormat = new TextLayoutFormat();
formatNormal.color = 0xaaaaaa;
var firstAnchor:int = logArea.text.length;
logArea.appendText(text);
var secondAnchor:int = logArea.text.length;
logArea.setFormatOfRange(formatHighlight, firstAnchor, secondAnchor);
logArea.setFormatOfRange(formatNormal, secondAnchor, secondAnchor);
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;
   
&amp;lt;mx:ViewStack id="contentStack" width="100%" height="100%" &amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Label id="labelSelected"&amp;gt;0 files selected&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" allowMultipleSelection="true" selectionColor="0xff0000" rollOverColor="0xff8888" /&amp;gt;
&amp;lt;s:Button label="Add folder" width="100%" click="addFolder();" /&amp;gt;
&amp;lt;s:Button label="Add files" width="100%" click="addFiles();" /&amp;gt;
&amp;lt;s:Button label="{'Remove ' + tileList.selectedItems.length + ' items'}" width="100%" enabled="{tileList.selectedItems.length&amp;gt;0}" click="removeSelected();" /&amp;gt;
&amp;lt;s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" /&amp;gt;

&amp;lt;s:Label&amp;gt;Resize options:&amp;lt;/s:Label&amp;gt;

&amp;lt;mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Width:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" /&amp;gt;
&amp;lt;mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Height:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/&amp;gt;
&amp;lt;mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output file names:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:TextInput id="nameInput" width="240" text="%initialName%" /&amp;gt;
&amp;lt;s:Button width="35" label="?" click="contentStack.selectedIndex = 2;" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output destination:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" /&amp;gt;
&amp;lt;s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle" width="100%"&amp;gt;
&amp;lt;s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" /&amp;gt;
&amp;lt;s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output format:&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output JPG quality:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Button label="Resize" width="100%" click="beginResize();" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" top="10" left="10"&amp;gt;
&amp;lt;s:VGroup width="100%" height="410" gap="20"&amp;gt;
&amp;lt;s:Label fontSize="20" width="100%" fontWeight="bold"&amp;gt;Output file names&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;You can build names for output files using provided wildcards and combining them with text.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;For example, "%initialName%_new" will generate names like "pic_new.jpg", "img_new.png", etc.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Available wildcards are:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%initialName%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Puts the initial name of the file that's being resized.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%num%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Gives each file a unique id number starting from 1.&amp;lt;/s:Label&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:Button label="Back" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" height="440" top="10" left="10"&amp;gt;
&amp;lt;s:Label width="100%" color="0xff3333" fontWeight="bold"&amp;gt;Do not add, remove or rename selected files.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Group width="100%"&amp;gt;
&amp;lt;mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" /&amp;gt;
&amp;lt;s:Label top="20" left="200" textAlign="right" id="statusText" fontWeight="bold"/&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;s:TextArea id="logArea" editable="false" width="100%" height="100%" /&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="buttonError" label="Save error log" click="saveErrors();" width="136" /&amp;gt;
&amp;lt;s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:ViewStack&amp;gt;
&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-2922092656149947975?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/HFf8W1ZLrWU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/2922092656149947975/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_29.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2922092656149947975?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/2922092656149947975?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/HFf8W1ZLrWU/kirsizer-flex-air-image-sizer-app-part_29.html" title="KirSizer - Flex AIR Image Sizer app: Part 13" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_29.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMERnk_eip7ImA9WhVWFUQ.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-1335135311435779351</id><published>2012-04-28T10:00:00.000+03:00</published><updated>2012-04-28T10:00:07.742+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-28T10:00:07.742+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSizer step-by-step" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><title>KirSizer - Flex AIR Image Sizer app: Part 12</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/hTQIGdj2fd3aXBPeQ99VWDLNi7w/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hTQIGdj2fd3aXBPeQ99VWDLNi7w/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/hTQIGdj2fd3aXBPeQ99VWDLNi7w/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hTQIGdj2fd3aXBPeQ99VWDLNi7w/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;In this tutorial we will work on catching and displaying errors.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
The first thing we'll do is declare 2 variables we're going to use - errorText and successFiles:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private var errorText:String;
private var successFiles:int;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The errorText value will represent the text of the error log text file that will be exported if the user wants to look at the errors in details.&lt;br /&gt;
&lt;br /&gt;
The successFiles value is a number that represents how many files have been successfully resized without errors.&lt;br /&gt;
&lt;br /&gt;
Go to the buttonError button and set its click event handler to saveErrors():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:Button id="buttonError" label="Save error log" click="saveErrors();" width="136" /&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
The function itself creates a file object with pre-defined file name errors.txt, we use the browseForSave() method to call out the browse dialog window. When the destination is selected, we check if there is no .txt in the file name and add it ourselves if it is so. Then we write the text into the file using FileStream's writeUTFBytes() method:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function saveErrors():void {
var file:File = File.desktopDirectory.resolvePath("errors.txt");
file.browseForSave("Save error log");
file.addEventListener(Event.SELECT, errorSelect);

function errorSelect(evt:Event):void {
if (!file.nativePath.match(/^.*\.(txt)$/i)) {
file.nativePath += ".txt";
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeUTFBytes(errorText);
fileStream.close();
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now let's start writing code to catch and report all kinds of errors that can happen. The first place we'll do that is in the beginResize() function's last if statement. Here, we set errorText to display the number of total files that are being resized, as well as the date and time when the operation was started. We use File.lineEnding to make sure we put the correct new line character. Also set successFiles to 0 here:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
nextAction();
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, the resizeNext() function. Here, we will check if file does not equal null before refering to it, and we create a new if statement that checks if file equals null. If it does, we simply set canProceed to false and call nextAction(). We don't need to report any errors here because if the file equals null, that means it is either an empty array, or an empty folder, and we will make sure that this kind of error is recorded before resizeNext() is even called.&lt;br /&gt;
&lt;br /&gt;
However, in the "File not found" and "Incorrect extension" if statements we should update the errorText string by adding a line with the error and the file's full path.&lt;br /&gt;
&lt;br /&gt;
In the end of resizeNext(), in the onTimer() function, increase successFiles by 1:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) {
currentNum++;
}
}

if(file!=null) logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (file != null &amp;&amp; !file.exists) {
canProceed = false;
logArea.appendText("ERROR: File not found\n");
errorText += "File not found at location: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}


// Error: bad extension
if (file != null &amp;&amp; (file.exists &amp;&amp; file.extension.toLowerCase() != "png" &amp;&amp; file.extension.toLowerCase() != "jpg" &amp;&amp; file.extension.toLowerCase() != "jpeg")) {
canProceed = false;
logArea.appendText("ERROR: Incorrect extension\n");
errorText += "Incorrect file extension: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}

if (file == null) { 
canProceed = false
nextAction();
};

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
var timer:Timer = new Timer(500, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}

function onTimer(evt:TimerEvent):void {
// new name
var newName:String = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
var newExtension:String;
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
var currentW:int = loader.content.width;
var currentH:int = loader.content.height;
var newW:int;
var newH:int;
var ratio:Number;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH &amp;gt; newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();
successFiles++;

nextAction();
}

}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
In nextAction() function, we also put text into the errorText value whenever we do so with logArea object. Moreover, we will add a few new if statements. The first one is located inside the other if statement, which also contains a loop that goes through all folder elements. After the loop, we add this if statement that checks if the selected file's "num" property equals the length of folderArray. Because if it doesn't, that means there are not enough image files in the folder than it initially was, or there are too many of them than there should be. We warn the user of this, but not abort the operation. All the images that were found will be resized. The ones that weren't found will just be ignored.&lt;br /&gt;
&lt;br /&gt;
Also, we need to remember that the folder itself could not exist. We need to check that in the big if statement with the loop before executing it, and after that statement we check if a folder is supposed to be resized now and it doesn't exist. If that happens, we warn the user and call nextAction(). We abort the function after that using return;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
logArea.appendText("Error: incorrect amount of image files found in folder " + folder.name + "\n");
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
logArea.appendText("Error: folder not found (" + folder.name + ")\n");
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
logArea.appendText("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now our application can catch some most common errors and create an error log file to help the user see what was the problem. Full code:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer" creationComplete="init();"&amp;gt;
   
&amp;lt;fx:Declarations&amp;gt;
&amp;lt;mx:ArrayCollection id="measures"&amp;gt;
&amp;lt;fx:String&amp;gt;%&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;px&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="actions"&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, proportional height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional sizes to fit specified sizes&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="formats"&amp;gt;
&amp;lt;fx:String&amp;gt;Same format as initial file&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to JPG&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to PNG&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="300"/&amp;gt;
&amp;lt;mx:Fade id="fadeOut" alphaFrom="1" alphaTo="0" duration="300"/&amp;gt;
&amp;lt;mx:TitleWindow id="waitWindow" title="Please wait" width="240" height="70" showCloseButton="false"&amp;gt;
&amp;lt;s:Group width="100%" height="100%"&amp;gt;
&amp;lt;s:Label top="10" left="10" id="waitLabel" width="220" color="0x000000" /&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Style&amp;gt;
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

#contentStack{
backgroundColor: #313131;
}

s|Label{
color: #fcfcfc;
}

s|Button{
chromeColor: #636363;
}

mx|ComboBox{
chromeColor: #636363;
color: #fcfcfc;
contentBackgroundColor: #000000;
rollOverColor: #aaaaaa;
selectionColor: #ffffff;
}

#logArea{
contentBackgroundColor: #111111;
focusedTextSelectionColor: #0000ff;
fontFamily: "Courier New";
color: #aaaaaa;
}
&amp;lt;/fx:Style&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.FileListEvent;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.system.ImageDecodingPolicy;
import flash.utils.ByteArray;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.controls.Image;
import mx.effects.easing.Linear;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
import mx.events.StateChangeEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import mx.managers.PopUpManager;

[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);
private var totalFiles:int;
private var currentNum:int;
private var totalErrors:int;
private var folderArray:Array = [];
private var destinationPicked:String = "";
private var errorText:String;
private var successFiles:int;

private function init():void {
addEventListener(KeyboardEvent.KEY_DOWN, keybDown);
}

private function keybDown(evt:KeyboardEvent):void {
if (evt.ctrlKey &amp;&amp; evt.keyCode == 65) {
var arr:Array = [];
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
arr.push(i);
}
tileList.selectedIndices = arr;
}
}

private function actionChange():void{
switch (actionCombo.selectedIndex) {
case 0:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 1:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = false;
heightMeasure.enabled = false;
break;
case 2:
newWidth.enabled = false;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 3:
newWidth.enabled = true;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = false;
widthMeasure.selectedIndex = 1;
heightMeasure.selectedIndex = 1;
}
}

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

private function filesSelected(evt:FileListEvent):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting files...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFiles(evt.files);
PopUpManager.removePopUp(waitWindow);
}
}

private function addFolder():void {
var file:File = new File();
file.browseForDirectory("Select a directory");
file.addEventListener(Event.SELECT, folderSelected);
}

private function folderSelected(evt:Event):void {
var file:File = evt.currentTarget as File;
Alert.show("Do you want to select subfolders too?", "Recursive selection?", Alert.YES | Alert.NO, null, warningClose);

function warningClose(ev:CloseEvent):void {
if (ev.detail == Alert.YES) {
startFolder(file, true);
}
if (ev.detail == Alert.NO) {
startFolder(file, false);
}
}
}

private function startFolder(file:File, recurs:Boolean):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting folders...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFolder(file, recurs);
PopUpManager.removePopUp(waitWindow);
}
}

private function doFiles(files:Array):void {
for (var i:int = 0; i &amp;lt; files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u &amp;lt; selectedFiles.length; u++) {
if (selectedFiles[u].type == 0 &amp;&amp; selectedFiles[u].path == files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem({type:0, path:files[i].nativePath});
}
updateTotalFiles();
}

private function doFolder(file:File, isRecursive:Boolean):void {
var picturesInFolder:int = 0;
var childFiles:Array = file.getDirectoryListing();
for (var i:int = 0; i &amp;lt; childFiles.length; i++) {
if (childFiles[i].extension == "png" || childFiles[i].extension == "jpg" || childFiles[i].extension == "jpeg") {
picturesInFolder++;
}
if (childFiles[i].isDirectory &amp;&amp; isRecursive) {
doFolder(childFiles[i], true);
}
}
if (picturesInFolder &amp;gt; 0) {
var alreadySelected:Boolean = false;
for (var a:int = 0; a &amp;lt; selectedFiles.length; a++) {
if (selectedFiles[a].type == 1 &amp;&amp; selectedFiles[a].path == file.nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem( { type:1, path:file.nativePath, name:file.name, num:picturesInFolder } );
updateTotalFiles();
}
}

private function updateTotalFiles():void {
totalFiles = 0;
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
if (selectedFiles[i].type==1) {
totalFiles += selectedFiles[i].num;
}else {
totalFiles++;
}
}
labelSelected.text = totalFiles + " files selected";
}

private function removeSelected():void {
while (tileList.selectedItems.length &amp;gt; 0) {
selectedFiles.removeItemAt(tileList.selectedIndices[0]);
}
updateTotalFiles();
}

private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') &amp;lt; 0 &amp;&amp; nameInput.text.indexOf('%num%') &amp;lt; 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected &amp;&amp; destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')&amp;gt;=0 || testName.indexOf("\\")&amp;gt;=0 || testName.indexOf('?')&amp;gt;=0 || testName.indexOf('%')&amp;gt;=0 ||
testName.indexOf('*')&amp;gt;=0 || testName.indexOf(':')&amp;gt;=0 || testName.indexOf('|')&amp;gt;=0 || testName.indexOf('"')&amp;gt;=0 ||
testName.indexOf('&amp;lt;') &amp;gt;= 0 || testName.indexOf('&amp;gt;') &amp;gt;= 0 || testName.indexOf('.') &amp;gt;= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" &amp;lt; &amp;gt; . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
successFiles = 0;
progressBar.setProgress(0, totalFiles);
errorText = "Error log of resizing operation of " + totalFiles + " files." + File.lineEnding + "Started at: " + new Date() + File.lineEnding;
errorText += "----------------------" + File.lineEnding;
nextAction();
}
}
}

private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) {
currentNum++;
}
}

if(file!=null) logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (file != null &amp;&amp; !file.exists) {
canProceed = false;
logArea.appendText("ERROR: File not found\n");
errorText += "File not found at location: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}


// Error: bad extension
if (file != null &amp;&amp; (file.exists &amp;&amp; file.extension.toLowerCase() != "png" &amp;&amp; file.extension.toLowerCase() != "jpg" &amp;&amp; file.extension.toLowerCase() != "jpeg")) {
canProceed = false;
logArea.appendText("ERROR: Incorrect extension\n");
errorText += "Incorrect file extension: " + file.nativePath + File.lineEnding;
totalErrors++;
nextAction();
}

if (file == null) { 
canProceed = false
nextAction();
};

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
var timer:Timer = new Timer(500, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}

function onTimer(evt:TimerEvent):void {
// new name
var newName:String = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
var newExtension:String;
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
var currentW:int = loader.content.width;
var currentH:int = loader.content.height;
var newW:int;
var newH:int;
var ratio:Number;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH &amp;gt; newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();
successFiles++;

nextAction();
}

}

private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
var isFolder:Boolean = false;
var folder:File = new File(selectedFiles[currentNum].path);
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
if (selectedFiles[currentNum].num != folderArray.length) {
logArea.appendText("Error: incorrect amount of image files found in folder " + folder.name + "\n");
errorText += "Not enough or too many image files found in folder " + folder.nativePath + " (some files were removed/added before it was this folder's turn to be resized)" + File.lineEnding;
totalErrors++;
}
}
if (!folder.exists &amp;&amp; selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
logArea.appendText("Error: folder not found (" + folder.name + ")\n");
errorText += "Folder was not found at " + folder.nativePath + File.lineEnding;
totalErrors++;
currentNum++;
nextAction();
return;
}
resizeNext(isFolder);
}else {
progressBar.setProgress(totalFiles, totalFiles);
logArea.appendText("Operation complete!\nErrors: " + totalErrors + "\nFiles resized: " + successFiles + "/" + totalFiles);
errorText += "----------------------" + File.lineEnding + "Total errors: " + totalErrors + File.lineEnding + "Files resized: " + successFiles + "/" + totalFiles;
buttonError.enabled = true;
buttonReturn.enabled = true;
}
}

private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}

private function saveErrors():void {
var file:File = File.desktopDirectory.resolvePath("errors.txt");
file.browseForSave("Save error log");
file.addEventListener(Event.SELECT, errorSelect);

function errorSelect(evt:Event):void {
if (!file.nativePath.match(/^.*\.(txt)$/i)) {
file.nativePath += ".txt";
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeUTFBytes(errorText);
fileStream.close();
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;
   
&amp;lt;mx:ViewStack id="contentStack" width="100%" height="100%" &amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Label id="labelSelected"&amp;gt;0 files selected&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" allowMultipleSelection="true" selectionColor="0xff0000" rollOverColor="0xff8888" /&amp;gt;
&amp;lt;s:Button label="Add folder" width="100%" click="addFolder();" /&amp;gt;
&amp;lt;s:Button label="Add files" width="100%" click="addFiles();" /&amp;gt;
&amp;lt;s:Button label="{'Remove ' + tileList.selectedItems.length + ' items'}" width="100%" enabled="{tileList.selectedItems.length&amp;gt;0}" click="removeSelected();" /&amp;gt;
&amp;lt;s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" /&amp;gt;

&amp;lt;s:Label&amp;gt;Resize options:&amp;lt;/s:Label&amp;gt;

&amp;lt;mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Width:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" /&amp;gt;
&amp;lt;mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Height:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/&amp;gt;
&amp;lt;mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output file names:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:TextInput id="nameInput" width="240" text="%initialName%" /&amp;gt;
&amp;lt;s:Button width="35" label="?" click="contentStack.selectedIndex = 2;" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output destination:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" /&amp;gt;
&amp;lt;s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle" width="100%"&amp;gt;
&amp;lt;s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" /&amp;gt;
&amp;lt;s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output format:&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output JPG quality:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Button label="Resize" width="100%" click="beginResize();" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" top="10" left="10"&amp;gt;
&amp;lt;s:VGroup width="100%" height="410" gap="20"&amp;gt;
&amp;lt;s:Label fontSize="20" width="100%" fontWeight="bold"&amp;gt;Output file names&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;You can build names for output files using provided wildcards and combining them with text.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;For example, "%initialName%_new" will generate names like "pic_new.jpg", "img_new.png", etc.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Available wildcards are:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%initialName%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Puts the initial name of the file that's being resized.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%num%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Gives each file a unique id number starting from 1.&amp;lt;/s:Label&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:Button label="Back" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" height="440" top="10" left="10"&amp;gt;
&amp;lt;s:Label width="100%" color="0xff3333" fontWeight="bold"&amp;gt;Do not add, remove or rename selected files.&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" /&amp;gt;
&amp;lt;s:TextArea id="logArea" editable="false" width="100%" height="100%" /&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="buttonError" label="Save error log" click="saveErrors();" width="136" /&amp;gt;
&amp;lt;s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:ViewStack&amp;gt;
&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-1335135311435779351?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/kE2S4m337Gw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/1335135311435779351/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_28.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/1335135311435779351?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/1335135311435779351?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/kE2S4m337Gw/kirsizer-flex-air-image-sizer-app-part_28.html" title="KirSizer - Flex AIR Image Sizer app: Part 12" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_28.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYFQn0_eip7ImA9WhVWFU4.&quot;"><id>tag:blogger.com,1999:blog-2631617199049210138.post-6856510064703843638</id><published>2012-04-27T10:00:00.000+03:00</published><updated>2012-04-27T16:41:53.342+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-27T16:41:53.342+03:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Flex 4" /><category scheme="http://www.blogger.com/atom/ns#" term="intermediate as3 tutorial" /><category scheme="http://www.blogger.com/atom/ns#" term="Making KirSizer step-by-step" /><category scheme="http://www.blogger.com/atom/ns#" term="Adobe AIR" /><title>KirSizer - Flex AIR Image Sizer app: Part 11</title><content type="html">
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/j_WRO1pj_ibC4OBVZZhDkCeMwi0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/j_WRO1pj_ibC4OBVZZhDkCeMwi0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/j_WRO1pj_ibC4OBVZZhDkCeMwi0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/j_WRO1pj_ibC4OBVZZhDkCeMwi0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;Today we will add the main feature for our resizing program - the resizing of images!&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
First we need to add the ability to select an output destination. Declare a variable destinationPicked, a String value with blank text as default:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private var destinationPicked:String = "";
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to the part where destination-related objects are put on screen and set the text input's id to destinationInput, and the "Browse" button's click event handler to pickDestination():&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:Label&amp;gt;Output destination:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" /&amp;gt;
&amp;lt;s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle" width="100%"&amp;gt;
&amp;lt;s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" /&amp;gt;
&amp;lt;s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Find the part where you create the JPG quality slider and set the id of the slider to qualitySlider:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;s:Label&amp;gt;Output JPG quality:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" /&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now go to beginResize() function and add a new if statement which doesn't let the user to start resizing if the destination is not set to "the same destination" and is not picked:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') &amp;lt; 0 &amp;&amp; nameInput.text.indexOf('%num%') &amp;lt; 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected &amp;&amp; destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')&amp;gt;=0 || testName.indexOf("\\")&amp;gt;=0 || testName.indexOf('?')&amp;gt;=0 || testName.indexOf('%')&amp;gt;=0 ||
testName.indexOf('*')&amp;gt;=0 || testName.indexOf(':')&amp;gt;=0 || testName.indexOf('|')&amp;gt;=0 || testName.indexOf('"')&amp;gt;=0 ||
testName.indexOf('&amp;lt;') &amp;gt;= 0 || testName.indexOf('&amp;gt;') &amp;gt;= 0 || testName.indexOf('.') &amp;gt;= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" &amp;lt; &amp;gt; . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
progressBar.setProgress(0, totalFiles);
nextAction();
}
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Create a new function pickDestination(), which sets destinationPicked value to the selected directory's nativePath:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now let's go to resizeNext() function and work on actually exporting the result files. We're only going to have to work in the internal onTimer() function.&lt;br /&gt;
&lt;br /&gt;
Firstly, let's fix the %num% wildcard changing to currentNum and not currentProgress, displaying incorrect number:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;newName = newName.replace("%num%", currentProgress);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Now, before we call nextAction() in the end of the function, let's add some code that creates a BitmapData object with the picture in it, resize it using a Matrix object, encode it using an approporiate encoder (depending on newExtension value), and save the output file to the correct destination.&lt;br /&gt;
&lt;br /&gt;
First we create the bitmap data object and add the image  to it using its draw() object. Create a Matrix object beforehand and use its scale() method to resize the picture:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then we check what extension we are saving the file in, and create an approporiate encoder object and encode the bitmap data into a byte array:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Then we pick the correct destination by checking which of the destination radio buttons is currently selected, and apply the correct path to a "newfile" object. Then we create a stream to write the bytes into the file:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
And we are done! The program can actually resize stuff now!&lt;br /&gt;
&lt;br /&gt;
&lt;div class="code"&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="300" height="460" 
showStatusBar="false" title="KirSizer" creationComplete="init();"&amp;gt;
   
&amp;lt;fx:Declarations&amp;gt;
&amp;lt;mx:ArrayCollection id="measures"&amp;gt;
&amp;lt;fx:String&amp;gt;%&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;px&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="actions"&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Fixed width, proportional height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional width, fixed height&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Proportional sizes to fit specified sizes&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:ArrayCollection id="formats"&amp;gt;
&amp;lt;fx:String&amp;gt;Same format as initial file&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to JPG&amp;lt;/fx:String&amp;gt;
&amp;lt;fx:String&amp;gt;Convert all to PNG&amp;lt;/fx:String&amp;gt;
&amp;lt;/mx:ArrayCollection&amp;gt;
&amp;lt;mx:Fade id="fadeIn" alphaFrom="0" alphaTo="1" duration="300"/&amp;gt;
&amp;lt;mx:Fade id="fadeOut" alphaFrom="1" alphaTo="0" duration="300"/&amp;gt;
&amp;lt;mx:TitleWindow id="waitWindow" title="Please wait" width="240" height="70" showCloseButton="false"&amp;gt;
&amp;lt;s:Group width="100%" height="100%"&amp;gt;
&amp;lt;s:Label top="10" left="10" id="waitLabel" width="220" color="0x000000" /&amp;gt;
&amp;lt;/s:Group&amp;gt;
&amp;lt;/mx:TitleWindow&amp;gt;
&amp;lt;/fx:Declarations&amp;gt;

&amp;lt;fx:Style&amp;gt;
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";

#contentStack{
backgroundColor: #313131;
}

s|Label{
color: #fcfcfc;
}

s|Button{
chromeColor: #636363;
}

mx|ComboBox{
chromeColor: #636363;
color: #fcfcfc;
contentBackgroundColor: #000000;
rollOverColor: #aaaaaa;
selectionColor: #ffffff;
}

#logArea{
contentBackgroundColor: #111111;
focusedTextSelectionColor: #0000ff;
fontFamily: "Courier New";
color: #aaaaaa;
}
&amp;lt;/fx:Style&amp;gt;

&amp;lt;fx:Script&amp;gt;
&amp;lt;![CDATA[
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.events.FileListEvent;
import flash.events.KeyboardEvent;
import flash.events.TimerEvent;
import flash.filesystem.File;
import flash.filesystem.FileStream;
import flash.geom.Matrix;
import flash.net.FileFilter;
import flash.net.URLRequest;
import flash.system.ImageDecodingPolicy;
import flash.utils.ByteArray;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.controls.Image;
import mx.effects.easing.Linear;
import mx.controls.Alert;
import mx.events.CloseEvent;
import mx.events.FlexEvent;
import mx.events.StateChangeEvent;
import mx.graphics.codec.IImageEncoder;
import mx.graphics.codec.JPEGEncoder;
import mx.graphics.codec.PNGEncoder;
import mx.managers.PopUpManager;

[Bindable]
private var selectedFiles:ArrayCollection = new ArrayCollection([]);
private var totalFiles:int;
private var currentNum:int;
private var totalErrors:int;
private var folderArray:Array = [];
private var destinationPicked:String = "";

private function init():void {
addEventListener(KeyboardEvent.KEY_DOWN, keybDown);
}

private function keybDown(evt:KeyboardEvent):void {
if (evt.ctrlKey &amp;&amp; evt.keyCode == 65) {
var arr:Array = [];
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
arr.push(i);
}
tileList.selectedIndices = arr;
}
}

private function actionChange():void{
switch (actionCombo.selectedIndex) {
case 0:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 1:
newWidth.enabled = true;
widthMeasure.enabled = true;
newHeight.enabled = false;
heightMeasure.enabled = false;
break;
case 2:
newWidth.enabled = false;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = true;
break;
case 3:
newWidth.enabled = true;
widthMeasure.enabled = false;
newHeight.enabled = true;
heightMeasure.enabled = false;
widthMeasure.selectedIndex = 1;
heightMeasure.selectedIndex = 1;
}
}

private function addFiles():void {
var file:File = new File();
file.browseForOpenMultiple("Select JPG or PNG files", [new FileFilter("Pictures", "*.jpg;*.jpeg;*.png")]);
file.addEventListener(FileListEvent.SELECT_MULTIPLE, filesSelected);
}

private function filesSelected(evt:FileListEvent):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting files...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFiles(evt.files);
PopUpManager.removePopUp(waitWindow);
}
}

private function addFolder():void {
var file:File = new File();
file.browseForDirectory("Select a directory");
file.addEventListener(Event.SELECT, folderSelected);
}

private function folderSelected(evt:Event):void {
var file:File = evt.currentTarget as File;
Alert.show("Do you want to select subfolders too?", "Recursive selection?", Alert.YES | Alert.NO, null, warningClose);

function warningClose(ev:CloseEvent):void {
if (ev.detail == Alert.YES) {
startFolder(file, true);
}
if (ev.detail == Alert.NO) {
startFolder(file, false);
}
}
}

private function startFolder(file:File, recurs:Boolean):void {
PopUpManager.addPopUp(waitWindow, this);
PopUpManager.centerPopUp(waitWindow);
waitLabel.text = "Selecting folders...";
var timer:Timer = new Timer(100, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onWait);
timer.start();
function onWait(ev:TimerEvent):void {
doFolder(file, recurs);
PopUpManager.removePopUp(waitWindow);
}
}

private function doFiles(files:Array):void {
for (var i:int = 0; i &amp;lt; files.length; i++) {
var alreadySelected:Boolean = false;
for (var u:int = 0; u &amp;lt; selectedFiles.length; u++) {
if (selectedFiles[u].type == 0 &amp;&amp; selectedFiles[u].path == files[i].nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem({type:0, path:files[i].nativePath});
}
updateTotalFiles();
}

private function doFolder(file:File, isRecursive:Boolean):void {
var picturesInFolder:int = 0;
var childFiles:Array = file.getDirectoryListing();
for (var i:int = 0; i &amp;lt; childFiles.length; i++) {
if (childFiles[i].extension == "png" || childFiles[i].extension == "jpg" || childFiles[i].extension == "jpeg") {
picturesInFolder++;
}
if (childFiles[i].isDirectory &amp;&amp; isRecursive) {
doFolder(childFiles[i], true);
}
}
if (picturesInFolder &amp;gt; 0) {
var alreadySelected:Boolean = false;
for (var a:int = 0; a &amp;lt; selectedFiles.length; a++) {
if (selectedFiles[a].type == 1 &amp;&amp; selectedFiles[a].path == file.nativePath) {
alreadySelected = true;
}
}
if (!alreadySelected) selectedFiles.addItem( { type:1, path:file.nativePath, name:file.name, num:picturesInFolder } );
updateTotalFiles();
}
}

private function updateTotalFiles():void {
totalFiles = 0;
for (var i:int = 0; i &amp;lt; selectedFiles.length; i++) {
if (selectedFiles[i].type==1) {
totalFiles += selectedFiles[i].num;
}else {
totalFiles++;
}
}
labelSelected.text = totalFiles + " files selected";
}

private function removeSelected():void {
while (tileList.selectedItems.length &amp;gt; 0) {
selectedFiles.removeItemAt(tileList.selectedIndices[0]);
}
updateTotalFiles();
}

private function beginResize():void {
var canProceed:Boolean = true;
if (selectedFiles.length == 0) {
canProceed = false;
Alert.show("No files or folders are selected.", "Can't start resizing!");
}
if (nameInput.text.indexOf('%initialName%') &amp;lt; 0 &amp;&amp; nameInput.text.indexOf('%num%') &amp;lt; 0) {
canProceed = false;
Alert.show("No wildcards were used in the name field! They are essential for each output file to have an unique name.", "Can't start resizing!");
}
if (newDestination.selected &amp;&amp; destinationPicked == "") {
canProceed = false;
Alert.show("No destination set!", "Can't start resizing!");
}
var testName:String = nameInput.text.replace("%initialName%", "");
testName = testName.replace("%num%", "");
if(testName.indexOf('/')&amp;gt;=0 || testName.indexOf("\\")&amp;gt;=0 || testName.indexOf('?')&amp;gt;=0 || testName.indexOf('%')&amp;gt;=0 ||
testName.indexOf('*')&amp;gt;=0 || testName.indexOf(':')&amp;gt;=0 || testName.indexOf('|')&amp;gt;=0 || testName.indexOf('"')&amp;gt;=0 ||
testName.indexOf('&amp;lt;') &amp;gt;= 0 || testName.indexOf('&amp;gt;') &amp;gt;= 0 || testName.indexOf('.') &amp;gt;= 0) {
canProceed = false;
Alert.show("Illegal characters in the name field! Make sure file name field does not contain these characters: / \ ? % * : | \" &amp;lt; &amp;gt; . ", "Can't start resizing!");
}
if (canProceed) {
contentStack.selectedIndex = 3;
var timer:Timer = new Timer(400, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
function onTimer(evt:TimerEvent):void {
buttonError.enabled = false;
buttonReturn.enabled = false;
logArea.text = "";
logArea.appendText("Beginning resizing of " + totalFiles + " files...\n");
currentNum = 0;
totalErrors = 0;
progressBar.setProgress(0, totalFiles);
nextAction();
}
}
}

private function resizeNext(isFolder:Boolean):void {
var file:File;
var canProceed:Boolean = true;
var loader:Loader;
var currentProgress:int = progressBar.value + 1;
progressBar.setProgress(currentProgress, totalFiles);

// if its a file
if (!isFolder){
currentNum++;
file = new File(selectedFiles[currentNum - 1].path);
}

// if its a folder
if (isFolder) {
file = folderArray.pop();
if (folderArray.length == 0) currentNum++;
}

logArea.appendText("#" + currentProgress + " (" + file.name + ")\n");

// Error: file not found
if (!file.exists) {
canProceed = false;
logArea.appendText("ERROR: File not found\n");
totalErrors++;
nextAction();
}


// Error: bad extension
if (file.exists &amp;&amp; file.extension.toLowerCase() != "png" &amp;&amp; file.extension.toLowerCase() != "jpg" &amp;&amp; file.extension.toLowerCase() != "jpeg") {
canProceed = false;
logArea.appendText("ERROR: Incorrect extension\n");
totalErrors++;
nextAction();
}

// load image
if (canProceed) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest(file.url));
}

// start timer
function onLoadComplete(evt:Event):void {
var timer:Timer = new Timer(500, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimer);
timer.start();
}

function onTimer(evt:TimerEvent):void {
// new name
var newName:String = nameInput.text.replace("%initialName%", file.name.substr(0, file.name.lastIndexOf('.')));
newName = newName.replace("%num%", currentProgress);

// new extension
var newExtension:String;
if (formatCombo.selectedIndex == 0) newExtension = file.extension;
if (formatCombo.selectedIndex == 1) newExtension = "jpg";
if (formatCombo.selectedIndex == 2) newExtension = "png";

// new size
var currentW:int = loader.content.width;
var currentH:int = loader.content.height;
var newW:int;
var newH:int;
var ratio:Number;

if (actionCombo.selectedIndex == 0) {
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
}

if (actionCombo.selectedIndex == 1) {
ratio = currentW / currentH;
if (widthMeasure.selectedIndex == 0) newW = newWidth.value/100 * currentW;
if (widthMeasure.selectedIndex == 1) newW = newWidth.value;
newH = newW / ratio;
}

if (actionCombo.selectedIndex == 2) {
ratio = currentW / currentH;
if (heightMeasure.selectedIndex == 0) newH = newHeight.value/100 * currentH;
if (heightMeasure.selectedIndex == 1) newH = newHeight.value;
newW = newH * ratio;
}

if (actionCombo.selectedIndex == 3) {
ratio = currentW / currentH;
newW = newWidth.value;
newH = newW / ratio;
if (newH &amp;gt; newHeight.value) {
newH = newHeight.value;
newW = ratio * newH;
}
}

// log
logArea.appendText("Resized from " + currentW + "x" + currentH + " to " + newW + "x" + newH + "\n");
logArea.appendText("Renamed to " + newName + "." + newExtension + "\n");

// extract
var matrix:Matrix = new Matrix();
matrix.scale(newW/currentW, newH/currentH);
var bitmapData:BitmapData = new BitmapData(newW, newH, false, 0xffffff);
bitmapData.draw(loader.content, matrix, null, null, null, true);

var encoder:IImageEncoder;
if (newExtension == "jpg") encoder = new JPEGEncoder(qualitySlider.value);
if (newExtension == "png") encoder = new PNGEncoder();
var byteArray:ByteArray = encoder.encode(bitmapData);

var newfile:File;
if (newDestination.selected) newfile = new File(destinationPicked + File.separator + newName + "." + newExtension);
if (oldDestination.selected) newfile = new File(file.parent.nativePath + File.separator + newName + "." + newExtension);
var stream:FileStream = new FileStream();
stream.open(newfile, FileMode.WRITE);
stream.writeBytes(byteArray);
stream.close();

nextAction();
}

}

private function nextAction():void {
if (currentNum &amp;lt; selectedFiles.length) {
var isFolder:Boolean = false;
// if its a folder, create a global array that stores all image files of the folder
if (folderArray.length &amp;gt; 0) isFolder = true;
if (selectedFiles.length &amp;gt; currentNum &amp;&amp; selectedFiles[currentNum].type == 1 &amp;&amp; folderArray.length == 0) {
isFolder = true;
folderArray = [];
var folder:File = new File(selectedFiles[currentNum].path);
var allContent:Array = folder.getDirectoryListing();
for (var i:int = 0; i &amp;lt; allContent.length; i++) {
if (allContent[i].isDirectory == false &amp;&amp; (allContent[i].extension.toLowerCase() == "png" || allContent[i].extension.toLowerCase() == "jpg" || allContent[i].extension.toLowerCase() == "jpeg")) {
folderArray.push(allContent[i]);
}
}
}
resizeNext(isFolder);
}else {
logArea.appendText("Operation complete!");
buttonError.enabled = true;
buttonReturn.enabled = true;
}
}

private function pickDestination():void {
var file:File = new File();
file.browseForDirectory("Select ouput destination");
file.addEventListener(Event.SELECT, outputSelect);
function outputSelect(evt:Event):void {
destinationPicked = file.nativePath;
destinationInput.text = destinationPicked;
}
}
]]&amp;gt;
&amp;lt;/fx:Script&amp;gt;
   
&amp;lt;mx:ViewStack id="contentStack" width="100%" height="100%" &amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Label id="labelSelected"&amp;gt;0 files selected&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:TileList id="tileList" width="282" height="100%" dataProvider="{selectedFiles}" itemRenderer="TileRenderer" columnWidth="60" rowHeight="60" columnCount="4" allowMultipleSelection="true" selectionColor="0xff0000" rollOverColor="0xff8888" /&amp;gt;
&amp;lt;s:Button label="Add folder" width="100%" click="addFolder();" /&amp;gt;
&amp;lt;s:Button label="Add files" width="100%" click="addFiles();" /&amp;gt;
&amp;lt;s:Button label="{'Remove ' + tileList.selectedItems.length + ' items'}" width="100%" enabled="{tileList.selectedItems.length&amp;gt;0}" click="removeSelected();" /&amp;gt;
&amp;lt;s:Button label="Continue" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10"&amp;gt;
&amp;lt;s:Button label="Return to file selection" width="100%" click="contentStack.selectedIndex = 0;" /&amp;gt;

&amp;lt;s:Label&amp;gt;Resize options:&amp;lt;/s:Label&amp;gt;

&amp;lt;mx:ComboBox width="100%" id="actionCombo" height="22" dataProvider="{actions}" selectedIndex="0" editable="false" change="actionChange();"
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Width:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newWidth" height="22" width="150" minimum="1" value="100" maximum="{(widthMeasure.selectedIndex==0)?(100):(4000)}" /&amp;gt;
&amp;lt;mx:ComboBox id="widthMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:Label width="50"&amp;gt;Height:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:NumericStepper id="newHeight" height="22" width="150" minimum="1" value="100" maximum="{(heightMeasure.selectedIndex==0)?(100):(4000)}"/&amp;gt;
&amp;lt;mx:ComboBox id="heightMeasure" height="22" width="50" dataProvider="{measures}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output file names:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:TextInput id="nameInput" width="240" text="%initialName%" /&amp;gt;
&amp;lt;s:Button width="35" label="?" click="contentStack.selectedIndex = 2;" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output destination:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle"&amp;gt;
&amp;lt;s:RadioButton id="oldDestination" label="Same directory" groupName="destinationGroup" selected="true" /&amp;gt;
&amp;lt;s:RadioButton id="newDestination" label="Specified directory" groupName="destinationGroup" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;s:HGroup verticalAlign="middle" width="100%"&amp;gt;
&amp;lt;s:TextInput width="100%" id="destinationInput" enabled="{newDestination.selected}" text="Select destination..." editable="false" /&amp;gt;
&amp;lt;s:Button width="80" label="Browse" enabled="{newDestination.selected}" click="pickDestination();" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output format:&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ComboBox width="100%" height="22" id="formatCombo" dataProvider="{formats}" selectedIndex="0" editable="false" 
openEasingFunction="Linear.easeOut" closeEasingFunction="Linear.easeIn" openDuration="300" closeDuration="300"/&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Label&amp;gt;Output JPG quality:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:HSlider id="qualitySlider" width="100%" minimum="1" maximum="100" value="100" /&amp;gt;

&amp;lt;s:Label/&amp;gt;

&amp;lt;s:Button label="Resize" width="100%" click="beginResize();" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" top="10" left="10"&amp;gt;
&amp;lt;s:VGroup width="100%" height="410" gap="20"&amp;gt;
&amp;lt;s:Label fontSize="20" width="100%" fontWeight="bold"&amp;gt;Output file names&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;You can build names for output files using provided wildcards and combining them with text.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;For example, "%initialName%_new" will generate names like "pic_new.jpg", "img_new.png", etc.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Available wildcards are:&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%initialName%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Puts the initial name of the file that's being resized.&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label fontSize="18" width="100%" fontWeight="bold"&amp;gt;%num%&amp;lt;/s:Label&amp;gt;
&amp;lt;s:Label width="100%"&amp;gt;Gives each file a unique id number starting from 1.&amp;lt;/s:Label&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;s:Button label="Back" width="100%" click="contentStack.selectedIndex = 1;" /&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;s:NavigatorContent width="100%" height="100%" hideEffect="fadeOut" showEffect="fadeIn"&amp;gt;
&amp;lt;s:VGroup width="280" height="440" top="10" left="10"&amp;gt;
&amp;lt;s:Label width="100%" color="0xff3333" fontWeight="bold"&amp;gt;Do not add, remove or rename selected files.&amp;lt;/s:Label&amp;gt;
&amp;lt;mx:ProgressBar id="progressBar" width="100%" mode="manual" label="Resizing %1/%2" color="0xffffff" /&amp;gt;
&amp;lt;s:TextArea id="logArea" editable="false" width="100%" height="100%" /&amp;gt;
&amp;lt;s:HGroup&amp;gt;
&amp;lt;s:Button id="buttonError" label="Save error log" width="136" /&amp;gt;
&amp;lt;s:Button id="buttonReturn" label="Return" click="contentStack.selectedIndex = 0;" width="136" /&amp;gt;
&amp;lt;/s:HGroup&amp;gt;
&amp;lt;/s:VGroup&amp;gt;
&amp;lt;/s:NavigatorContent&amp;gt;
&amp;lt;/mx:ViewStack&amp;gt;
&amp;lt;/s:WindowedApplication&amp;gt;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2631617199049210138-6856510064703843638?l=kirill-poletaev.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/KirillPoletaev/~4/10PHX1HP55s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://kirill-poletaev.blogspot.com/feeds/6856510064703843638/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_27.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6856510064703843638?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/2631617199049210138/posts/default/6856510064703843638?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/KirillPoletaev/~3/10PHX1HP55s/kirsizer-flex-air-image-sizer-app-part_27.html" title="KirSizer - Flex AIR Image Sizer app: Part 11" /><author><name>Kirill Poletaev</name><uri>http://www.blogger.com/profile/10345000564393362213</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="20" src="http://1.bp.blogspot.com/_l1TO0E4U6fA/S9Hf-vF3wSI/AAAAAAAAABw/HUFpPKEajsQ/S220/dealwithit.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://kirill-poletaev.blogspot.com/2012/04/kirsizer-flex-air-image-sizer-app-part_27.html</feedburner:origLink></entry></feed>

