Runtime Revolution
 
Articles Other News

Revolution 2.9 Developments

by Mark Waddingham

Introduction

Revolution 2.9 is set to be one of the most significant Revolution releases in recent years. Not only will it be delivered containing a record number of bug fixes and stability improvements, but also with a whole host of new features allowing you to write even better applications more easily.

As the development cycle for this mammoth release draws to a close, I thought it would be worthwhile to give a quick whirlwind tour of some of the new feature areas present in the 2.9 engine.

Linux

Although Revolution has always supported Linux for both development and deployment, the features on this platform have always lagged behind the other platforms. This is no longer the case.

We've done our best to try and utilize as many of the features of modern Linux GNOME desktops as possible, and the Linux version of Revolution now has almost complete feature parity with the other platforms. More importantly, the engine still runs on a wide variety of distributions both new and old, and features will 'gracefully degrade' if a given distribution does not support something.

Some of the things you will now find supported in Linux are:

  • alpha-masked windows if your distribution has the newest 'compositing window manager' feature
  • complex unicode text rendering support
  • antialiased text rendering
  • all the graphics features introduced in version 2.7
  • native file, printing and color dialogs
  • backdrop

All in all, this should mean it's even easier to take an application written on Windows and Mac OS X and deploy it for Linux!

Drag-Drop

Drag-drop is a ubiquitous UI interaction paradigm on modern Desktops. Although Revolution has always supported drag-drop to some extent, with 2.9 you will find an expanded collection of syntax to deal with this aspect of application development.

Perhaps the most important improvement to drag-drop handling is the introduction of the dragStart message.

Previously, to initiate a drag-drop operation you would set the dragData global property in a mouseDown handler:

on mouseDown
-- Set the dragData to indicate we want a drag-drop operation to start
set the dragData["text"] to "Hello World!"
-- Drag-drop operation will commence as soon as this handler finishes executing
end mouseDown

However, this had the result that the drag-drop operation would commence immediately on the mouse button being pressed – rather than after the mouse has moved a certain distance.

The dragStart message solves this problem. The engine will first send a mouseDown message to a control and then, if the user moves the mouse at least the dragDelta pixels away from the starting point, will then send a dragStart message. Most importantly, a drag-drop operation will only commence if the dragData is not empty after the dragStart message has finished executing:

on dragStart
-- This message is sent after the control has received a mouseDown message
-- and the user has moved the mouse at least 'the dragDelta' pixels

-- Set the dragData to indicate we want a drag-drop operation to start

set the dragData["text"] to "Hello World!"

-- Drag-drop operation will commence as soon as this handler finishes executing
end dragStart

One thing worth mentioning is that no drag-drop operation starts until a dragStart handler has been sent – regardless of whether it has been handled by a control or not. The side-effect of this is that older applications using the mouseDown method for drag-drop will continue to work, and indeed will work better – setting the dragData in mouseDown won't have any effect unless the user moves the mouse dragDelta pixels and dragStart is sent!

Printing

Producing hard-copy is one of the most critical features for many desktop applications. However, while Revolution has always supported printing, it has never been all that good at manipulating page or printing options. With 2.9 this is no longer the case: there is now a rich collection of syntax for changing both paper and printer device settings, as well as storing and retrieving those which have been chosen by the user through the use of standard printer dialogs. One typical ability of applications is to 'remember' the user's last used printer settings on a per document basis. For example, you could use the following handler in response to do a Print... menu item:

on printWithDialog
-- Make sure printer dialog displays the settings last saved with the document
set the printerSettings to the uPrinterSettings[the platform] of stack "myDocument"

-- Show the printer dialog
answer printer
if the result is
"cancel" then
exit printWithDialog
end if

-- Update the printer settings stored with the document to those chosen by the user
set the uPrinterSettings[the platform] of stack "myDocument" to the printerSettings

-- Tell the document stack to print itself
send "printDocument" to stack "myDocument"

end printWithDialog

Here we assume that our 'document' is a stack myDocument and that it will print itself in response to receiving a printDocument message. You will see here that we are storing the printerSettings on a per-platform basis – this is because the printer settings string from one platform cannot be used to configure a printer on another.

Active Scripting

A long standing feature of Revolution on Mac OS X has been the ability to easily execute AppleScript using the alternate language variant of the do command. This is can be extremely useful because Applescript is supported by many Mac OS X applications for external scripting and automation. For example, you can instruct the Finder to open a window to the startup-disk with something like:

local tApplescript
put "tell application" && quote & "Finder" & quote & return after tApplescript
put "activate" & return after tApplescript
put "make new Finder window to startup disk" & return after tApplescript
put "end tell"
do tApplescript as "applescript"

Up until now, there has been no analogue for this feature on Windows – however, with 2.9 comes the ability to use any Window Active Scripting Languge in the alternate language variant of do. At first glance this may not seem like a particularly useful feature, until you realize most active scripting languages allow easy creation of OLE/Automation objects.

For example, it is really easy to control iTunes using VBScript. The following simple code snippet will start the current song in iTunes playing:

local tVBScript
put "Dim iTunesApp" & return after tVBScript
put "Set iTunesApp = CreateObject(" & quote & "iTunes.Application" & quote & ")" & \
return after tVBScript
put "iTunesApp.CurrentTrack.Play()" after tVBScript
do tVBScript as "vbscript"

While the following code snippet will stop the current song playing:

local tVBScript
put "Dim iTunesApp" & return after tVBScript
put "Set iTunesApp = CreateObject(" & quote & "iTunes.Application" & quote & ")" & \
return after tVBScript
put "iTunesApp.Stop()" after tVBScript
do tVBScript as "vbscript"

Its worth digging around and seeing what applications you have on your system also allow such control – you might be surprised as to what you can do!

New Operators

Readability of code is one of Revolution's biggest strong points, and 2.9 sees some welcome additions to the core language syntax for manipulating both strings and arrays.

Two string operations that are very common are testing whether a string begins or ends with another string. Previously, a string prefix (begins with) test would need an expression like:

char 1 to (the length of tPrefix) of tString is tPrefix

and a string suffix (ends with) test would need an expression like:

char -(the length of tSuffix) to -1 of tString is tSuffix

Neither of these expressions is particular obvious however – particularly when reading through code. In 2.9, however there are two new binary operators to deal with these tests:

tString begins with tPrefix
tString ends with tSuffix

Both doing exactly what you would expect!

Arrays have also seen a couple of new operators that improve readability of code using them, but also offer significant performance advantages over older methods.

The first of these operators is the is an array operator. This is a unary operator that returns true if the given expression can be treated as an array. For example:

put empty into tEmptyArray
answer tEmptyArray is an array -- true
put "hello" into tString
answer tString is an array -- false
put "foo" into tArray["bar"]
answer tArray is an array -- true

Previously, you would need to have used an expression like:

tVariable is empty or the keys of tVariable is not empty

Not only does this operator give a nice enhancement to readability, it is also much faster than the old method as it has no need to construct a string containing all the keys of the array!

The other array operator that has been introduced is is among the keys of. This operator tests for existence of a key in a given array:

put 1 into tArray["foo"]
put 2 into tArray["baz"]
answer "foo" is among the keys of tArray -- true
answer "bar" is among the keys of tArray –- false

Again not only does this give readability advantages over the previously incantation (tKey is among the lines of the keys of tArray), but is also much faster (almost constant time, rather than taking time proportional to the number of keys)!

Databases

Another area that has got a fair amount of attention for 2.9 is database access. Indeed, we have resolved a significant number of issues with revdb (Revolution's low level database layer) as well as the higher level database library and Database Query Builder.

Perhaps the most notable improvement is to the variable binding facility available when using calls such as revExecuteSQL and revQueryDatabase. The idea behind variable binding is that you can write a query with placeholders for data that you wish to pass to the database, and then indicate which variables to take data from for them. For example, an insert query using binding might look something like:

put "hello" into tBar
put "world" into tBaz
revExecuteSQL tId, "INSERT INTO foo(bar, baz) VALUES (:1, :2)", "tBar", "tBaz"

This would insert a new row into the table foo with column bar containing the string "hello" and column baz containing the string "world". In this example we are using separate variables to bind the data, but you can also use an equivalent form:

put "hello" into tDataArray[1]
put "world" into tDataArray[2]
revExecuteSQL tId, "INSERT INTO foo(bar, baz) VALUES (:1, :2)", "tDataArray"

Here the placeholder index is used as a lookup into the keys of tDataArray. One important thing to note about the implementation of variable binding in 2.9 is that it automatically processes any data before sending to ensure it appears verbatim – previously you would have had to have 'escaped' the data. However, you do still need to explicitly indicate if the data you wish to bind is binary.

To indicate that a variable contains binary data just prefix its name with *b. For example:

revExecuteSQL tId, "INSERT INTO foo(bar, baz) VALUES (:1, :2)", "*btBar", "tBaz"

Would indicate that the tBar variable should be treated as containing binary data. You can also indicate whether individual elements of an array binding are binary by prefixing individual keys with *b. For example:

put tBinaryValue into tDataArray["*b1"]

Would indicate that when binding using tDataArray the first place-holder should be treated as binary data.

Summary

What I've covered in this article is just the tip of the iceberg in terms of what 2.9 contains as a release. In addition to the features talked about above, this release will also contain an unprecedented number of bug fixes and stability improvements. If you haven't gotten around to giving 2.9 a try yet, why not sign up for the Open Beta and give it a try?

-
 
©2008 Runtime Revolution Ltd, 15-19 York Place, Edinburgh, Scotland, UK, EH1 3EB.
Questions? Email info@runrev.com for answers.