This is just a little tip for anyone who has recently switched from Windows to Mac OS X and was wondering where the “Lock Workstation” shortcut went. This shows you how to set up a timeout using the screen saver as well.
You should now see a little padlock in menu bar close to the clock. When you click this, and menu will drop down, and the first menu item Lock Screen will allow you to do just that.
Now if you would like to lock the screen on a time out, follow these instructions:
Unfortunately there is no keyboard shortcut for locking a workstation, and there is no way – at this time at least – to set up a shortcut to perform this particular function.
UPDATE: Adobe recently released BrowserLab … If you want a fast method of previewing your sites on different versions of IE running on different OSes, you might want to check it out
After recently weening myself off Windows and on to Mac OS X – and deleting my Windows partition – it became pretty clear that if I would need some method of running Internet Explorer on my Mac if I wanted to continue doing web development with any kind of success. After a bit of Googling, a bit of testing, and healthy dose of “issues”, I have come up with a fast, stable – and best of all free – method of running IE6, IE7 and IE8 simultaneously on my Intel Mac legally.
First of all, you will need to download a bunch of software. I have listed all of this below:
Next, I put together a list of steps that need to be followed in order to get this all working. Read though it first, as I have attached the reasons why I have done things a certain way to help you get a feel for what is going on.
/Applications/Q.app/Contents/MacOS/qemu-img convert -O raw -f vpc OldVHDImage.vhd NewRAWImage.raw VBoxManage convertdd NewRAWImage.raw NewVDIImage.vdi
cd \WINDOWS\system32\drivers ren processr.sys processr.old exit
If all goes well, you should have three virtual machines that can all run together synchronously for some serious browser testing. If you would like a little more information, I would suggest checking out Jeff Couturier’s article (he has a nice tutorial video on the site too), and Jeremy Gillick’s article.
UPDATE: It appears this article has been translated into Danish by Rasmus Luckow-Nielsen. Thanks Rasmus!
If you haven’t seen it already, check out the fancy new Konductor website. We’ve implemented the beautiful design that Casey did for us, and we have also added a bunch of new content as well. If you were wanting a little more information on who Konductor is targeted at, and what it brings to the table, make sure you take a peek. The new site is also running on Konductor, so check it out if you want to get a feel for the type of code it generates.
We also moved the Konductor blog and forums, and we are in the process of making these look pretty too.
The forums are now at http://forums.konductor.net/, rather than http://www.konductor.net/forums/. The new skin is up and looking good
The blog is now at http://blog.konductor.net/, rather than http://www.konductor.net/blog/. The new skin will be up in the next couple of weeks.
As far as the application goes, we have made a lot of progress, and we keep getting more and more excited with what we have. This is going to turn a lot of heads when we go live…
XULRunner is a project that has been around since the inception of Firefox, and predates Adobe AIR by quiet a margin. For those who have not really looked into it too much, XULRunner has a lot of similarities to Adobe AIR – it is a cross platform runtime that allows you to run code written in XUL – an XML derivative very similar to HTML.
A little more information: XUL elements add certain features that are required to add things like menus, menu items, toolbars, toolbar buttons and the like into your application, and can be mixed and matched with HTML. XULRunner also exposes extra functions to JavaScript, allowing you to access files on the host computer, read and write from SQLite databases, and do a lot of things that AIR does in this regard.
I haven’t played with XUL a whole lot since AIR came out, as documentation has always fallen behind AIR’s. And performing some advanced functions, like populating a tree component with SQLite results, or manipulating an image, are a little more involved than with AIR.
However, XULRunner has been really branching out to a lot of platform – Windows, Mac OS and Linux support was all there before AIR even came on the scene. There is also SkyOS support, and today the first “pre-alpha” of XULRunner is available for Windows Mobile (codenamed “Fennec”). There are also lightweight ports available for mobile Linux distros, and for Symbian S60.
This really opens a lot of doors for developers – your desktop applications and mobile applications can all share the same codebase. XUL itself is actually quiet nice to write in, and I was always impressed by how it always took advantage of native OS components libraries.
Anyway, I think the XULRunner team deserves very high praise for what they have accomplished – they have always been ahead of the game when with their platform independent runtime, and they are continuing to push support to an impressive array of platforms.
I have been writing some applications in AIR that for the first time make extensive use of SQLite. I hit a few roadblocks along the way, so here are a few things that may come in handy if you are using SQLite in AIR for the first time.
Rather than creating a new SQLStatement object for each query you want to run, it is recommended that you reuse an existing instance, and just alter the parameters associated with the object. You will reap three benefits from doing this:
For queries that get used across an entire application, I have found that putting the SQLConnection object and wrapper functions for the SQLStatement objects inside a singleton class seems to work very well.
NOTE: Every time you change the text property, the query will get recompiled. The trick is in using the parameters.
The following example demonstrates this:
package
{
import flash.data.SQLConnection;
import flash.data.SQLStatement;
public class SQLConnectionWrapper
{
private static const SINGLETON_INSTANCE:SQLConnectionWrapper = new SQLConnectionWrapper(SingletonLock);
public var connection:SQLConnection;
private var selectRecord:SQLStatement;
public static function get instance():SQLConnectionWrapper
{
return SINGLETON_INSTANCE;
}
public function SQLConnectionWrapper(lock:Class)
{
// This ensures that only once instance of this class may be created and accessed
if(lock != SingletonLock){
throw new Error("Class Cannot Be Instantiated: Use SQLConnectionWrapper.instance");
}
createDatabase();
}
private function createDatabase():void
{
// This creates an SQLConnection object , which can be accessed publicly so that event listeners can be defined for it
connection = new SQLConnection();
var databaseFile:File = File.applicationStorageDirectory.resolvePath("database.db");
connection.openAsync(databaseFile);
}
public function getRecord(recordId:int):SQLStatement
{
// If selectRecord has not been instantiated, then create the instance with all the data that it needs
// If it has been instantiated, then we can skip over this part and take advantage of the fact that it has now been cached
if(!(selectRecord is SQLStatement)){
selectRecord= new SQLStatement();
selectRecord.sqlConnection = connection;
selectRecord.text =
"SELECT record_id, description, is_active " +
"FROM record_tbl " +
"WHERE record_id = :recordId";
}
// This simply changes the one parameter that needs to be changed
// Because recordId has already been declared as an int, this will be converted into an SQLite recognized integer
selectRecord.parameters[":recordId"] = recordId;
return selectRecord;
}
}
}
// This little class exists to stop extra instances on the singleton being created
class SingletonLock{}
One major problem that caught me off guard when trying to reuse instances was event handling. If I were to use SQLConnectionWrapper.instance.getRecord() in multiple places across my application, and attach event listeners to them, I will encounter a problem where all three event listeners get triggered. Even when each event listener is removed, errors still occur due to the fact that the SQLStatement object still thinks that it is executing. Not pretty.
Using Responder objects allows to avoid the pain that come s from constantly adding and removing event listeners, and prevent the race condition that occurs with the SQLStatement objects. The Responder object allows you to define event handlers for both SQLEvent.RESULT and SQLErrorEvent.ERROR events. And rather than attaching itself to the SQLStatement object, it will only handle events generated for each particular call to SQLStatement.execute().
NOTE: After a successful query, the handler will be passed an SQLResult object as the first argument, and not an SQLEvent object.
NOTE: After a failed query, the handler will be passed an SQLError object as the first argument, and not an SQLErrorEvent object.
Here is an example:
package
{
import SQLConnectionWrapper;
import flash.net.Responder;
import flash.data.SQLResult;
public class SampleApplication
{
// Create the responder object once, so that it can be reused again and again
private var responder:Responder = new Responder(handleSuccess, handleFailure);
public function SampleApplication():void
{
// This opens the database - obviously
SQLConnectionWrapper.instance.connection.addEventListener(SQLEvent.OPEN, executeQuery);
SQLConnectionWrapper.instance.connection.open();
}
private function executeQuery():void
{
// This statement essentially creates a reference to the SQLStatement object that is created once for all time within the SQLConnectionWrapper singleton
var statement:SQLStatement = SQLConnectionWrapper.instance.getRecord(1);
statement.execute(-1, responder);
}
private function handleSuccess(result:SQLResult):void
{
trace("Record ID: " + result.data[0].record_id + ", Description: " + result.data[0].description + ", Is Active: " + result.data[0].is_active);
}
private function handleFailure(error:SQLError):void
{
trace("Epic Fail: " + error.message);
}
}
}
When returning results from a SELECT query, any SQLite Boolean objects will be correctly converted into ActionScript Boolean objects. However, when executing an INSERT or UPDATE query, an integer is actually required. This quick and nasty workaround demonstrates what has to take place:
This would need to be part of SQLConnectionWrapper
private function setRecord(description:String, isActive:Boolean):void
{
if(!(insertRecord is SQLStatement)){
insertRecord = new SQLStatement();
insertRecord.sqlConnection = connection;
insertRecord.text = "INSERT INTO record_tbl (description, is_active) VALUES (:description, :isActive)";
}
insertRecord.parameters[":description"] = description;
// This is pretty straightforward, but it simply converts the ActionScript Boolean into an int, so that SQLite can accurately create it's own boolean in the database
insertRecord.parameters[":isActive"] = isActive ? 1 : 0;
return insertRecord;
}
When executing an INSERT or UPDATE query, dates will be inserted into the database as expected (well, kind of – if you really want to know more about that then just ask about it). In the case of Date objects, though, the troubles occur when trying to return Date objects from a SELECT query. I won’t try and explain this one, because there is already a great explanation of what is happening here.
What I will do is provide some code that demonstrates the quickest way of getting a usable Date object from an SQLite database:
NOTE: The String object that gets returned is formatted so that it can be passed to the constructor of a Date object without having to jump through any additional hoops.
This would need to be part of SQLConnectionWrapper
public function getAllBookings():SQLStatement
{
if(!(selectBookings is SQLStatement)){
selectBookings = new SQLStatement();
selectBookings.sqlConnection = connection;
// This statement will return the date as a MM/DD/YYYY formatted string
selectBookings.text =
"SELECT booking_id, record_id, STRFTIME('%m/%d/%Y', checkout_date) AS checkout_date, STRFTIME('%m/%d/%Y', checkin_date) AS checkin_date " +
"FROM booking_tbl";
}
return selectBookings;
}
UPDATE: Paul Robertson has posted an update about this. Apparently setting the column type to DATE, rather than DATETIME, will allow you to use Flex Date objects natively. You can see this alluded to in the LiveDocs.
Do you want to store images in your database? Do you want to pull them out as DisplayObjects, ready to do with as you wish? Well, here is some code that demonstrates the entire process of loading an image from a file, storing it in your SQLite database, retrieving the image data, and converting it back into a DisplayObject:
NOTE: ByteArray objects do not need any special attention when being either SELECTed from or INSERTed into the database.
This would need to be part of the application code
import flash.display.Bitmap;
import flash.display.Loader;
import flash.filesystem.File;
import flash.net.URLLoader;
import flash.utils.ByteArray;
import mx.graphics.codec.PNGEncoder;
private function selectPicture():void
{
// This little section here creates a file object, and then launches the file browser so that you can select your image
var file:File = File.documentsDirectory;
file.addEventListener(Event.SELECT, handleSelectPicture);
file.browseForOpen("Select Picture");
}
private function handleSelectPicture(event:Event):void
{
// Once the image file has been selected, we now have to load it
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoadPicture);
loader.load(new URLRequest(event.target.url));
}
private function handleLoadPicture(event:Event):void
{
// The first thing that we do is create a Loader object (which is a subclass od DisplayObject)
var loader:Loader = Loader(event.target.loader);
// Next, we cast the loader as a Bitmpa object, as the Bitmap object has function to return a BitmapData object based on the image
var image:Bitmap = Bitmap(loader.content);
var encoder:PNGEncoder = new PNGEncoder();
// The PNGEncoder allows you to convert BitmapData object into a ByteArray, ready for storage in an SQLite blob field
var byteArray:ByteArray = encoder.encode(image.bitmapData);
var statement:SQLStatement = SQLConnectionWrapper.instance.setPicture(1, byteArray);
statement.execute(-1, responder);
}
This would need to be part of SQLConnectionWrapper
private function setPicture(recordId:String, byteArray:ByteArray):void
{
if(!(insertRecord is SQLStatement)){
insertRecord = new SQLStatement();
insertRecord.sqlConnection = connection;
insertRecord.text = "INSERT INTO picture_tbl (record_id, data) VALUES (:recordId, :byteArray)";
}
insertRecord.parameters[":recordId"] = recordId;
// The ByteArray should be added as a parameter; this makes the whole process of storing the image in the blob field very easy
insertRecord.parameters[":byteArray"] = byteArray;
return insertRecord;
}
This would need to be part of the application code
import mx.controls.Image;
// This function would be defined in a Responder object that handles a successful query of picture_tbl
private function handleSuccess(result:SQLResult):void
{
var image:Image = new Image();
image.addEventListener(Event.COMPLETE, handleLoadPicture);
image.load(result.data[0].picture);
}
private function handleLoadPicture(event:Event):void
{
var picture:DisplayObject = DisplayObject(event.target.content);
}
Sorry, I was started to get a bit tired of writing towards the end of this article
If you want me to elaborate on anything, just leave a comment. Hopefully the examples are enough to get you up and running with SQLite, whilst avoiding most of the gotchas that come along with it
Now that the new stable is available for download, I would imagine that a new release build isn’t too far off…
You can download the latest version of the SDK here: http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+3
From what I can tell, it mostly comprises of bug fixes – no fancy new functionality seems to have been added. But I guess you will have to find out for yourself by trawling through the nightly release notes (joy :S)… If you find anything noteworthy, let me know, I only noticed a few changes in some of the utility classes.
Don’t worry, the whole links thing won’t become a regular feature of this blog – this is just a roundup of a few things I have wanted to post about recently but just didn’t have the time:
It has been a little barren on the blog lately, but I have a few posts I really want to write up about some really cool stuff we are doing with Dreamweaver extension development, so subscribe to the RSS feed and keep your eye open for it if you are into that kind of thing.
I changed the blog theme, so now my blog looks a little more legit. I will be making tweaks to it as I go, but I think now it is a bit of an improvement over the standard WordPress theme that was there initially.
Also, FYI, if you are subscribed using RSS, the feed URL has been changed to http://feeds.feedburner.com/andrewodri. All of your orignal feed links should now be redirecting, however.
Also, I am on an Adobe Developer Connection video. I am the guy who say profound things like “awesome” and “Flash Catalyst”:
I usually find geeky hip-hop annoying and somewhat embarrassing. This one is not only funny, It’s incredibly informative too. I think it speaks for itself:
There is an awesome write up on the Teknision blog regarding their work on the Konductor AIR application.
Gabor, John and especially Tony (I am always biased toward the developer
) have really helped shape Konductor since beginning of the project. We started out with an idea, and, with their experience in planning and developing good UX, along with their ideas and discussions on how to apply AIR’s unique features, Konductor was really brought to life. We now have a slick application that is being rolled out to the public.
Not to sound like a broken record, but if you’re at Adobe MAX this year, come check our booth out. You can play with the application, and talk with Teknision and ourselves to find out how accomplished a lot of this. I’m sure you will be just as excited as we are about it!