Issue 46 * April 10, 2008

Rolling up your sleeves with Rev 2.9
How to use drag-drop, printing, and other new commands in your projects

by Laura Grinton and Tim Shields

The new Revolution 2.9 brings several new enhancements to the language allowing you to achieve higher quality results and in many cases simplify your code and make things easier. Here’s a hands-on article that shows you step-by-step how to implement these new commands in your solutions. You’ll learn how to do drag-and-drop, how to manage printers and output configuration, and view examples of other new commands.

Drag-and-Drop
The drag-and-drop features have been overhauled in this version of Revolution. The acceptDrop property has been replaced by the dragAction property and many new features have been introduced. The dragAction property is set by the source object and represents the action which should be taken when the object is dropped. It can take one of four values; "none", "move", "link" or "copy".

New features that have been introduced include the dragImage, dragDelta and the allowableDragActions

  • The dragImage is an image which will transparently follow the cursor position during a drag operation, it can be set to a number corresponding to an image id in the image library or the long id of an image object.
  • The dragDelta specifies the number of pixels the cursor must be moved by with the mouse button pressed before a drag operation is instantiated.
  • The allowableDragActions is set by the source object to specify which drag actions it allows to be carried out upon the drag data.
  • The dragData property itself also has a new "private" key. This should be used when the data will only be used locally i.e. the data within this key will not be visible to external applications.

Below is an example of how to use some of these features in a stack.

Drag-and-Drop Example – Recycling
In this example we will create a recycling game. The aim is to drag-and-drop the recyclable items into the correct recycling bins.

The Recycling stack is made up of three recycling bins and multiple smaller recyclable items. All the items are Revolution image objects. The recyclable items are "source" objects, in other words where the drag-drop operations begin. The code for a 'cardboard' item is shown below:

on dragStart
   local tWidth,tHeight
   -- We want to say what type the object is so we can
   -- determine if it is being placed in the right bin.
   set the dragData["private"] to "cardboard"
   -- The dragImage is set to the image of the source.
   set the dragImage to the id of me
   put the width of me into tWidth
   put the height of me into tHeight
   put tWidth / 2 into tWidth
   put tHeight / 2 into tHeight
   -- The dragImageOffset specifies where the dragImage
   -- will be relative to the cursor. For this example
   -- the centre of the dragImage is beneath the cursor.
   set the dragImageOffset to tWidth,tHeight
   -- We specify that the drag data should be moved, so
   -- it will look like it was placed in the bin.
   set the allowableDragActions to "move"
end dragStart 

We set the private key to the type of object the recycle item is. This key will be checked by the target object to ensure that the item being dropped is the same as the type of items it is meant to be collecting (i.e. accepting). If the item matches the bin type, it will accept the drop.

The target object checks the item is the correct type by comparing the private dragData key with the name of the item type it is collecting. If the item is of the correct type it then checks the allowableDragActions to see what it can do with it. It will always move the object by preference, then copy, then link. If the object is of an incorrect type or if the allowableDragActions is empty, the dragAction will be set to "none". This specifies that no action should be taken.

on dragEnter
   -- Check the recycle item is of the correct type.
   if dragData["private"] is "cardboard" then
      -- We set the itemdelimiter so we can distinguish between the
      -- allowable drag actions.
      set the itemdelimiter to comma
      -- If we are allowed to move the object we want to do this
      -- - move
      -- is our prefered drag action.
      if "move" is among the items of the allowableDragActions then
         -- We set the dragAction to "move"
         set the dragAction to "move"
      else if "copy" is among the items of the \
      allowableDragActions then
         -- We can't move the object but we can copy it - this is
         -- our second
         -- prefered action.
         set the dragAction to "copy"
      else if "link" is among the items of the \
      allowableDragActions then
         -- We can only link to the source object.
         set the dragAction to "link"
      else
         -- We aren't allowed to do anything with the object.
         set the dragAction to "none"
      end if
   else
      -- The object is an incorrect type - we do not want to
      -- accept it.
      set the dragAction to "none"
   end if
end dragEnter

on dragDrop
   -- We check that we are accepting the item.
   if the dragAction is not "none" then
      -- and if we are change the value of the bottle
      -- label to tell the user we have accepted the drop.
      put "Accepted" into field "CardboardLabel"
      send "resetLabel" to me in 2 seconds
   end if
end dragDrop

command resetLabel
   put "CARDBOARD" into field "CardboardLabel"
end resetLabel 

A dragEnd message will be sent whether the drop has been successful or not. This is handled in the source scripts and, for the purpose of this tutorial, hides the items if the drops were successful. Normally on a "move" operation the original item will be deleted. In order to do this you must place the dragEnd handler outside the source item. A dragEnd handler from a source item however is shown below:

on dragEnd
   -- Check that the drop action succeeded.
   if the dragAction is not "none" then
      -- The source object should only be
      -- deleted if it was moved.
      switch the dragAction
         case "move"
            hide me
         break
         default
      end switch
   end if
end dragEnd 

You can download a copy of the sample stack by clicking here. Enjoy exploring!

Printing
Now we will move on to look at the ways in which Revolution’s printing features have been changed. A full explanation of the new syntax can be found in the documentation. Here is a summary of some of the features that were used to implement the new printer settings dialog. The Revolution print dialog can be accessed using the answer printer command. However, firstly the systemPrintSelector property must be set to false, ("set the systemPrintSelector to false") otherwise a print options dialog will be provided by the operating system.


Revolution’s Printer Setup Dialog

At the top of the dialog is the "Print Target" group. In the main scrolling window you will see a list of all the printers currently installed on the system. This makes use of the availablePrinters function which retrieves a return-delimited list of all available system printers. The printer icon highlighted in blue indicates the printer that will be used. The currently selected printer can be obtained using the printerName which can get and set the active system printer. If this property is set to "empty" the systems default printer will be used.

The check box below the main window allows you to output your document to a file instead of a printer device. Once this is checked the button to the right of the file path field can be pressed in order to browse to an appropriate save folder. This changes the printerOutput from "device" to "file:". Mac OS X will save the document in a pdf format, Windows Vista uses the xps format, Unix uses postscript format and other Windows versions will be system specific.

Next we come to the Print Range group. This operates in much the same way as it does in default system print dialogs. Selection of one of the three radio controls will set the printRanges to either "all" "selection" (Windows only) or "pages". The pages can be specified individually or as a range e.g. 1,3,6 or 1-5. Revolution will automatically handle overlapping ranges.

Note: A page in Revolution is represented by all printing up to either a "print break" command or a print card command that causes the card to flow over the height of the page.

In the Copies group you can choose the number of duplicate copies of your currently selected print range you wish to output. This is obviously only valid if you are printing to a device rather than a file. The printCopies is the property that allows you to get and set this value and should be an integer.

Toggling the collate option will update the graphic to the right indicating the order in which your pages will be printed. This is only relative when you are printing more than one copy of your document. When collate is selected the individual pages will be correctly ordered upon printing, otherwise groups of the same page will be printed together. Selecting the "Collate" checkbox will place "true" into the printCollate property. Otherwise the printCollate will be set to "false". Additional options includes the ability to print the document in black and white, by checking this box the printColors property will be set to false. Duplex printing controls the page orientation of double sided printing. It is governed by the printDuplex property which can take one of three values: "none", "short edge" or "long edge". When the duplex checkbox is not selected the value of this property will be "none". If checked, the "Long edge", "Short edge" radio buttons will be enabled, selecting each one will place the corresponding value into the printDuplex property. Selecting short edge will cause the second page to be rotated by 180 degrees, with long edge the back page will have the same orientation as the front page.

The use of these various settings is dependent on which of them is supported by the currently selected printer. When a printer is selected from the print target window the stack will query the printerFeatures of that device. This property contains a comma delimited list of features the printer supports, here are the possible features it can include:

  • collate: the printer supports collation
  • copies: the printer supports printing multiple copies
  • color: the printer supports printing in color
  • duplex: the printer can print in duplex mode

Finally when the OK or Apply buttons are pressed the changes will be made to the appropriate global variables. This covers most of the new syntax implementation. However there are further settings that have not been mentioned here which also have an effect on the printing output, for example the page orientation which can be set in the page setup dialog. For further information on the new printing features and syntax refer to the Revolution documentation or User guide.

Other New Commands
A number of new commands with a variety of functions have been introduced for 2.9, here is a summary of their implementation.

wrap
This function makes it easier (and more readable) for you to loop successively over a fixed number of items in a list. In other languages, you might use the mod operator, but since Revolution arrays (and other chunk elements) begin their number at 1 and not 0, wrap saves you the muss and fuss of writing an algebraic expression.

put "C,D,E,F,G,A,B" into tNotes
local tScale
repeat with x = 1 to 14
   put item (x wrap 7) of tNotes & return after tScale
end repeat 

tScale = C,D,E,F,G,A,B,C,D,E,F,G,A,B,

begins/ends with
The begins with operator takes two strings and returns true if the second string matches the beginning of the first string. Ends with performs the equivalent comparison on the end of the first string. See the example below:

"foobar" begins with "foo" -- evaluates to true
"foobar" ends with "foo" -- evaluates to false 

This function is affected by the state of the caseSensitive property which is either set to true or false, by default it is false.

is an array:
Can detect whether or not a variable currently holds an array. The is not form is also valid.

local tArray
put "test" into tArray[1]
put tArray is an array -- evaluates to true 

is [not] among the keys of:
This new form of is/is not, among the keys of can be applied to array variables. Since the keys can be assigned specific names as well as an index this is a quick and easy way to iterate through arrays and detect a specific entry

local tArray
put "World" into tArray["Hello"]
"Hello" is among the keys of tArray -- evaluates to true

The expression 'tString is among the keys of tArray' is almost equivalent to 'tString is among the lines of keys of tArray' except that the former can cope with keys that contain the return character.

There are more commands that have been added and some modifications that have been made to existing ones, full details of which can be found in the changelog, located in the Revolution engine folder. We hope this article has given you a good insight into the new release and you are now as excited about it as we are. Enjoy using Revolution 2.9!

Main Menu What's New