Download - Software at Speed of Sound -- Dan Shafer

Transcript
Page 1: Software at Speed of Sound -- Dan Shafer
Page 2: Software at Speed of Sound -- Dan Shafer

All rights reserved. No part of this publication may be reproduced or transmitted by any means, whether electronic, mechanical, photocopying, printing or otherwise, without the express prior written consent of the publisher and the copyright holder.

©2003, 2005 by Dan Shafer

First published in Great Britain in 2003. Second edition published in the United States, 2005

Published by Shafer Media, 5 Harris Court, Suite A-2, Monterey, California, USA 93940 http://www.shafermediastore.com

Page 3: Software at Speed of Sound -- Dan Shafer

i

Table of ContentsTable of Contents ............................................................................................................i

Acknowledgements ........................................................................................................ix

Preface..........................................................................................................................xii

Revolution in Context..........................................................................................................xii The Two Components of Revolution .............................................................................................. xii Revolution and Other Languages...................................................................................................xiii

Why I Wrote This Book .....................................................................................................xiii

Who Should Read This Book? ...........................................................................................xvi

What’s in This Book?.........................................................................................................xvi

On preReadBook ..........................................................................................................xx

A Few Words on Dan Shafer and the Making of Two Revolutions ...................................xx

Chapter 1: Wrappin’ My Head Around the Revolution................................................24

Designing the Counter Tutorial ............................................................................................2 Getting Started ................................................................................................................................. 2 Creating the Field for the Counter’s Value........................................................................................ 6 Saving for the First Time................................................................................................................ 10 Button Time................................................................................................................................... 13 Copy, Paste, and Tweak? Is That All There Is? ............................................................................... 19 Creating and Scripting the Reset Button.......................................................................................... 21

Where Do We Go From Here?............................................................................................23

Chapter 2: Basics of Stack Building.............................................................................25

Creating Stacks ...................................................................................................................25

Putting Stuff Into the Stack ................................................................................................27

Groups and Background Behaviors....................................................................................32 Grouping Objects ........................................................................................................................... 32

Saving a Stack .....................................................................................................................35 So What’s Tricky? ......................................................................................................................... 36

Creating Scripts...................................................................................................................36 Opening the Script Editor ............................................................................................................... 37

Chapter 3: Using and Managing Basic Building Blocks..............................................39

Picking Up Where We Left Off...........................................................................................39

Creating Meaningful Names for Objects ............................................................................39

Arranging Object Layouts ..................................................................................................44

Setting Properties for Revolution Objects ..........................................................................50 Common Types of Properties ......................................................................................................... 51

Running Alone is One Thing...............................................................................................54

Adding a Menubar to Your Application.............................................................................55

Page 4: Software at Speed of Sound -- Dan Shafer

ii

Menubars: An Overview ................................................................................................................ 55 The Revolution Menu Builder ........................................................................................................ 56

Chapter 4: Revolution Building Blocks........................................................................61

Naming Things in Transcript..............................................................................................61 Several words in one ...................................................................................................................... 61 The first character is important ....................................................................................................... 62

Revolution Application Structure .......................................................................................62

Active Elements of Transcript ............................................................................................62 Messages ....................................................................................................................................... 63 Commands..................................................................................................................................... 63 Functions ....................................................................................................................................... 63 Handlers ........................................................................................................................................ 63 Scripts............................................................................................................................................ 64

Passive Traits.......................................................................................................................64 Variables as containers of information ............................................................................................ 64 Properties....................................................................................................................................... 66 Control structures........................................................................................................................... 66 Chunks........................................................................................................................................... 67

Objects .................................................................................................................................67 Some Common Characteristics....................................................................................................... 67 Stacks ............................................................................................................................................ 68 Groups........................................................................................................................................... 70 Cards ............................................................................................................................................. 72 Fields............................................................................................................................................. 73 Buttons .......................................................................................................................................... 74 Images and Graphics ...................................................................................................................... 75 Scrollbars....................................................................................................................................... 76 Player ............................................................................................................................................ 79 Audio and Video Clips................................................................................................................... 81 EPS Object..................................................................................................................................... 82

Chapter 5: Transcript Basics........................................................................................83

Script Mechanics .................................................................................................................83

The script editor's menus ....................................................................................................84 The File menu................................................................................................................................ 84 The Edit menu................................................................................................................................ 85 The Script menu............................................................................................................................. 85 The View menu.............................................................................................................................. 86 Long lines...................................................................................................................................... 91 Comments...................................................................................................................................... 91 Setting Scripting Fonts ................................................................................................................... 92 Syntax checking............................................................................................................................. 93

Handlers ..............................................................................................................................93 Where messages originate .............................................................................................................. 93 A second type of handler ................................................................................................................ 94

Messages ..............................................................................................................................94 User-defined inheritance................................................................................................................. 96

Variables..............................................................................................................................97 Naming variables ........................................................................................................................... 97

Page 5: Software at Speed of Sound -- Dan Shafer

iii

Using variables .............................................................................................................................. 97 Displaying variables....................................................................................................................... 98 Assigning values to variables.......................................................................................................... 99

Containers .........................................................................................................................100 Using me and target as containers................................................................................................. 100 Selection as a container ................................................................................................................ 100 Message box as a container .......................................................................................................... 101

Addressing a field's contents .............................................................................................102 Items in a field ............................................................................................................................. 102 Characters and words in addresses................................................................................................ 103

Chapter 6: System Messages.......................................................................................106

Messages Galore!...............................................................................................................106

Who Gets the Message?.....................................................................................................106 Using send to direct a message ..................................................................................................... 107 Using pass to avoid message disappearance.................................................................................. 107

An Overview of System Messages .....................................................................................108

User-Interaction Messages ................................................................................................108 Mouse messages........................................................................................................................... 108 Keyboard messages...................................................................................................................... 108 Help-management message .......................................................................................................... 109 Menu message ............................................................................................................................. 109 Drag-and-Drop messages ............................................................................................................. 109 Player interaction messages.......................................................................................................... 109 Focus and selection-related messages ........................................................................................... 110 Object movement and sizing messages ......................................................................................... 110 Messages related to starting and stopping...................................................................................... 110

Revolution-Related messages ............................................................................................111

Handling system messages ................................................................................................111

Handling Mouse Messages ................................................................................................111 Handling mouse-button messages................................................................................................. 111

Laboratory: Four mouse-button handlers........................................................................112 Handling mouse-location messages .............................................................................................. 114

Laboratory: Popup field controlled by mouse..................................................................115

Handling keyboard messages............................................................................................116 Special field-related keyboard messages ....................................................................................... 119 Uses for keyboard messages ......................................................................................................... 119

Handling the Menu-Related Message ...............................................................................120

Handling the help Message ...............................................................................................120

Handling Control Management Messages........................................................................121 Object-related action messages..................................................................................................... 121 Stack Window Manipulation Messages......................................................................................... 123 Resizing and Moving Controls ..................................................................................................... 123

Handling Focus and Selection-Related Messages .............................................................124

Handling Drag-and-Drop Related Messages ....................................................................125

Page 6: Software at Speed of Sound -- Dan Shafer

iv

Handling Messages Related to Starting and Stopping .....................................................126

Handling Messages Related to Environmental Changes..................................................126

Handling Player Interaction Messages .............................................................................127

Revolution Messages ..................................................................................................128

Handling Stack-Usage Messages.......................................................................................128

The idle Message ...............................................................................................................128

The mainStackChanged Message .....................................................................................128

The Error-Related Messages.............................................................................................128

Chapter 7: Mouse and Keyboard Scripting ................................................................130

Mouse Management Overview..........................................................................................130 Monitoring the Mouse.................................................................................................................. 131 Has the Mouse Button Been Clicked?........................................................................................... 132 Where is the Mouse? .................................................................................................................... 134 Screen coordinates ....................................................................................................................... 134

Laboratory: Finding Mouse Locations .............................................................................134

One coordinate at a time ...................................................................................................137

Ending repeated commands..............................................................................................137

What is Under the Mouse?................................................................................................137 What Text is the Mouse Positioned Over?..................................................................................... 137 What Object is the Mouse Positioned Over? ................................................................................. 139 Mouse Movement and Control Boundaries ................................................................................... 139 Clicking the Mouse for the User ................................................................................................... 139

What's with the mouse? ....................................................................................................140

Laboratory: Is user holding down button?.......................................................................141

Keyboard Control and Monitoring...................................................................................141

Is That Key Down?............................................................................................................142

Laboratory: Keypresses Modify Results ..........................................................................142

Checking for two-key combinations .................................................................................143

Saving the key's condition.................................................................................................143

Watching User Keystrokes................................................................................................144

Chapter 8: Control Structures and Logical Operators ...............................................146

Loops and Conditions: Background .................................................................................146

If-Then Processing.............................................................................................................146 General format and use................................................................................................................. 147 Nesting if statements.................................................................................................................... 148

Laboratory: Nested if statements......................................................................................148

Avoiding Complex if Statements With switch ..................................................................149

Conditional Operators and Calculations ..........................................................................152 True or false tests ......................................................................................................................... 152

Page 7: Software at Speed of Sound -- Dan Shafer

v

Equality conditions....................................................................................................................... 152 Comparison operators................................................................................................................... 153 Inclusion ...................................................................................................................................... 153

Laboratory: Testing for inclusion.....................................................................................154 Status........................................................................................................................................... 155 Type ............................................................................................................................................ 155 Using the constants true and false ................................................................................................. 157

Looping Commands ..........................................................................................................159 Basic looping concepts................................................................................................................. 159 Basic repeat conditions................................................................................................................. 160 Using object-counting in repeat conditions.................................................................................... 160 The repeat for and Basic repeat commands ................................................................................... 161 The repeat with command ............................................................................................................ 162 The repeat while construct............................................................................................................ 163

Laboratory: Using the repeat while command.................................................................163 The repeat until command ............................................................................................................ 164 The naked repeat command.......................................................................................................... 165

Control Within repeat Loops .............................................................................................165 The next repeat command............................................................................................................. 165 Counting backwards with down to................................................................................................ 166 Defining a different increment...................................................................................................... 167 The exit repeat command ............................................................................................................. 167

Chapter 9: Card and Stack Management Methods.....................................................169

Navigation Commands ......................................................................................................169

Using go in a Script ...........................................................................................................170 Addressing a destination............................................................................................................... 170 Cards with special addresses......................................................................................................... 171 Nonexistent Stacks and Cards....................................................................................................... 172 Stack Management Techniques..................................................................................................... 173 Stack Window Management......................................................................................................... 175

Finding Cards by Content.................................................................................................176 What kind of match to find? ......................................................................................................... 177

Laboratory: Specifying the search....................................................................................178 Narrowing the search ................................................................................................................... 179 Limitations on find....................................................................................................................... 180 After the find is Over.................................................................................................................... 181

Marking Cards for Later Use ...........................................................................................181 Criteria for card marking .............................................................................................................. 181 What to do with marked cards ...................................................................................................... 182

Using pop and push in Scripts...........................................................................................183 The stack created by push............................................................................................................. 183 Using pop without showing the card ............................................................................................. 184 Using push and pop between objects............................................................................................. 184

Printing Cards: The Basics ...............................................................................................185

Chapter 10: Managing Text and Data Without Databases.........................................188

Revolution as an Information Base...................................................................................188 A Word About Main Stacks and Substacks ................................................................................... 189

Page 8: Software at Speed of Sound -- Dan Shafer

vi

Reading Information from a Field....................................................................................190 The get command......................................................................................................................... 190 Addressing reminder .................................................................................................................... 191 Using put commands to read data ................................................................................................. 191 Typical uses for put...................................................................................................................... 192

How Many Characters in the Field?.................................................................................192

Finding and Selecting Text................................................................................................193 Where did the find take place?...................................................................................................... 193 Using select to Highlight Arbitrary Text ....................................................................................... 193 Using select to position the cursor ................................................................................................ 193 Selecting everything and nothing.................................................................................................. 194 Combining select and find for data management ........................................................................... 194 Locating a sub-string's position with offset.................................................................................... 194

Laboratory: Using offset for field decomposition.............................................................195

Advanced Search Using matchText ..................................................................................196

Modifying the Contents of Fields ......................................................................................197 The type command....................................................................................................................... 197 The delete command .................................................................................................................... 198

Laboratory: Using delete...................................................................................................198

Changing Fields by Concatenation ...................................................................................200 Uses for concatenation ................................................................................................................. 200 Special constants for concatenation............................................................................................... 201

Working with Styled Text .................................................................................................202 Using HTML and RTF Formatted Text......................................................................................... 203

Sorting Stacks....................................................................................................................205 Selecting the sort direction ........................................................................................................... 206 Defining the data type .................................................................................................................. 206 Naming the sort field.................................................................................................................... 206 Confining the sort to marked cards ............................................................................................... 207 Sorting by function....................................................................................................................... 207 Some complex sorts ..................................................................................................................... 207 Sorting within fields..................................................................................................................... 207

Hypertext Techniques .......................................................................................................207

Laboratory: Hypertext experiment ..................................................................................208 Creating Destinations for Hyperlinks............................................................................................ 209

Date and Time: Special Data Types..................................................................................209 The date function ......................................................................................................................... 210 The time function ......................................................................................................................... 210 The seconds function.................................................................................................................... 211 The ticks function......................................................................................................................... 211 Using convert to reformat dates .................................................................................................... 211

Trapping the Return and Enter Keys...............................................................................212

Multiple Cards vs. Single Data Source .............................................................................213 Multiple-Card Solution................................................................................................................. 213 One-Card Solution ....................................................................................................................... 214

Text File Operations..........................................................................................................215

Page 9: Software at Speed of Sound -- Dan Shafer

vii

Traditional File I/O ...................................................................................................................... 216 URL-Based File Transactions....................................................................................................... 220

Chapter 11: Dialog Boxes...........................................................................................221

Dialogs and Revolution .....................................................................................................221 Three kinds of dialogs .................................................................................................................. 224 Four modal dialogs in Revolution ................................................................................................. 225

Using Dialogs in Transcript ..............................................................................................225

The answer Dialog.............................................................................................................226 Some rules about answer dialogs.................................................................................................. 226 Tailoring text in a single answer dialog......................................................................................... 227 Computer design consideration..................................................................................................... 227

The ask Dialog ...................................................................................................................227 Example of the ask command....................................................................................................... 228 Standard file dialogs in Revolution ............................................................................................... 228 The ask password variation .......................................................................................................... 229

Setting up a Printer ...........................................................................................................230

Displaying Dialogs as Sheets .............................................................................................230

Displaying an Icon in a Dialog ..........................................................................................230

Chapter 12: Managing Menus and Preparing for Distribution..................................231

Menu Scripting Strategy ...................................................................................................231

Scripting Menu Items........................................................................................................232 What Happens When the User Selects a Menu Item...................................................................... 232

Platform Differences .........................................................................................................237

Basic Steps to Preparing Standalones...............................................................................237 Choosing Your Standalone Settings.............................................................................................. 238 No Need to Do Much ................................................................................................................... 239 General Settings........................................................................................................................... 240 Stacks Settings............................................................................................................................. 241 Copy Files Setting........................................................................................................................ 242 Application-Specific Settings ....................................................................................................... 242 Determining How Your Users Report Bugs .................................................................................. 246

The End...For Now ............................................................................................................247

Afterword (or on postReadBook):...............................................................................248

Building a Real-World Application At the Speed of Thought .........................................248

ToDoPlus Design ...............................................................................................................249

Building the Menu System ................................................................................................250 Creating the Menus ...................................................................................................................... 251 Working With the Pre-Built Menus .............................................................................................. 252 Creating the ToDo Menu and Its Items ......................................................................................... 253 Generating the Menu Program Code............................................................................................. 254

Finding and Integrating a Calendar Object .....................................................................255

Designing the Text File for To Do Items...........................................................................257 What’s This order Thing About? .................................................................................................. 259

Page 10: Software at Speed of Sound -- Dan Shafer

viii

Laying Out the User Interface ..........................................................................................259 Creating the Calendar Navigation and Date-Selection Region ....................................................... 260 Creating the To Do List Management Region ............................................................................... 261

Creating the To Do List Item Management Area.............................................................266

Programming ToDoPlus Functionality.............................................................................272 Creating the To Do List File......................................................................................................... 273 Getting the Calendar to Display and Respond to Clicks................................................................. 274

Creating a New Task.........................................................................................................277 Setting Up for a New Task to Be Entered...................................................................................... 277 Connecting the Date-Setting Buttons ............................................................................................ 278

Setting Up the Application at Launch ..............................................................................279 Loading MyToDo.txt Into Memory .............................................................................................. 279 Adding the New Task................................................................................................................... 282 Updating the File.......................................................................................................................... 285

Editing, Updating and Deleting Tasks ..............................................................................286 Displaying the Task to Be Edited.................................................................................................. 286 Updating Task Status ................................................................................................................... 288 Deleting a Task ............................................................................................................................ 289

Filtering and Sorting the To Do List.................................................................................290 How Filtering Works.................................................................................................................... 290 Programming the Filtering Process ............................................................................................... 291

Sorting the To Do List in the View....................................................................................300

Hooking Up the Menus......................................................................................................300

Implementing Revolution’s Built-in Menus......................................................................302 Scripting the ToDo Menu............................................................................................................. 304 Scripting the Help Menu............................................................................................................... 305

That’s All, So What’s Next?..............................................................................................306

Page 11: Software at Speed of Sound -- Dan Shafer

ix

Acknowledgements

The Revolution may not be televised or even Webcast, but one thing about it is certain: it’s a huge team effort. In writing this book, I’ve been most fortunate to have the support and help of a much larger-than-usual number of people. And while I’ve tried to keep track as I went along of everyone who’s been helpful, I’m absolutely certain that I’ve forgotten at least one or two folks. For that oversight, I apologize.

First and foremost, my gratitude goes to the intrepid band of Advanced Guard Revolutionaries who volunteered to act as beta readers of this book as it took shape. I got a lot more volunteers for help than I felt I could adequately manage, but I sure picked the winners out of the lot!

These are the folks who read the early drafts, pointed out the holes and mistakes, debated with me issues of style and programming approach, and in general really helped shape the book. Without them, this book would be much less valuable than it is.

• Marty Billingsley, Chair, Department of Computer Science, University of Chicago Laboratory Schools, http://www.mbillilngsley.com

• Howard Bornstein of DesignEQ, http://www.designeq.com

• Katherine Cochrane, President, The CD-Info Company. http://www.cd-info.com & http://epublishing-info.com

• Rob Cozens, Chief Computer Wizard, Serendipity Software Co.,

http://www.oenolog.net

• Jonathan Feinstein of Shrink2Fit Software

• Richard Gaskin, Ambassador, Fourth World Media Corporation, http://www.fourthworld.com/

• Roger A. Kenyon, Ph.D., http://www.riverwoodpub.com/educatio.htm

• Jim Lambert

• Jacqueline Landman Gay of HyperActive Software, http://www.hyperactivesw.com

• Quentin Long of Graphic Descriptions

• Dave McKee, CTO of MorphStrategy

• Scott Raney, President, MetaCard Corporation, http://www.metacard.com

• Scott Slaugh

• Greg Tjosvold

The Revolution user mailing list has been an enormous source of helpful feedback, suggestions, encouragement, insights, and technical commentary (which often took the form of, “Not that way, you moron!” only nicer). More specifically, the following

Page 12: Software at Speed of Sound -- Dan Shafer

x

individuals have gone out of their way to be gracious and helpful as this book has unfolded:

• Dave Cragg

• Monte Goulding

• Pierre Sahores

• Sannyasin Sivakatirswami

• Sadhunathan Nadesan

• Richard Gaskin

Chipp Walters belongs on that list as well, but Chipp has become so much more helpful in so many ways that I wanted to single him out for a special vote of thanks.

At Runtime Revolution, the following people have been helpful, supportive and encouraging throughout this process, and I appreciate them more than I can say:

• Heather Williams

• Kevin Williams

• Tuviah Snyder

• Jeanne Devoto

An old HyperCard-era colleague, Ro Nagey, was the key instrumentality in putting together the agreement between me and Runtime Revolution that resulted in RunRev deciding to publish the printed version of this book as its first book publishing venture. Ro and I spent a lot of late nights joking, reminiscing, writing, debating and editing together over instant messaging channels. He was a source of inspiration and support and humor when those were needed.

For the Second Edition

A few people provided me with comments and feedback on the first edition of this book, but nobody came close to the Herculean effort put forth by Stephen Goldberg. Stephen marked up the entire book, wrote me a 10-page letter with comments and then made sure I got the whole thing so I could review it and incorporate his comments as appropriate. Thanks, Stephen! A good deal of what changed between the two versions is due directly to your input.

Page 13: Software at Speed of Sound -- Dan Shafer

xi

In all of my previous books (and there have been a few dozen by now), I have always expressed my appreciation to my wife, Carolyn, for putting up with the long hours of looking at the back of my head and conceding the book project’s hold on my attention. Now I just thank her for being her and for loving me and for being there whenever I need anything. Everyone who knows us knows we have a perfect marriage. She’s 100% of the reason.

Page 14: Software at Speed of Sound -- Dan Shafer

xii

Preface

Revolution in Context I make no bones about the fact that I love Revolution. To me, it is the ideal tool for solving a broad set of computer programming problems. Specifically, Revolution shows its true colors when it is used where:

• speed of development is more important than the number of college classes taught using the language

• speed of execution is an important consideration but not a case of life and death

• cross-platform deployment with little or no futzing with details is important

So would I use Revolution to develop a real-time monitoring system for the uranium rods in a nuclear reactor? Maybe not. But then I wouldn’t use C or Java for that, either, in all likelihood. I’d be more inclined to use assembly language. In this case, slow kills!

Would I use Revolution to develop a word processor? Probably not in its present incarnation, but then who wants to develop yet another word processor anyway? (These are the kinds of questions computer scientists like to ask about languages. I don’t know about their real-world relevance, but I don’t mind answering them, either.)

But there are very few other classes of software I wouldn’t be willing to trust to Revolution. From database applications to system utilities to programs running on Web servers, and lots of things in between, Revolution is a great tool.

The Two Components of Revolution Revolution has two components: an engine and an Integrated Development Environment (IDE). The engine includes the scripting language and the code that enables you to create stand-alone cross-platform applications. There is a separate engine for each target platform you might want to develop for: Macintosh Classic, Macintosh OS X, all 32-bit flavors of Windows, and most brands of Unix (Linux, BSD, etc.). The engine was originally developed by Scott Raney as MetaCard over the course of a decade before Runtime Revolution purchased the technology in mid-2003.

The IDE is the set of tools Runtime Revolution packages with the engine to make it possible to create and program sophisticated and richly interactive applications without writing a bunch of code. These tools include palettes, windows, object explorers, code editors, and a bunch of other cool stuff to support your programming efforts. The IDE is so completely separate from the engine that it is possible to create your own IDE that suits your particular programming style and interests if you want and largely or completely ignore the IDE provided with the product. In fact, several people have done just that. Two of the best-known and most widely used are Richard Gaskin’s Devolution (http://www.fourthworld.com/products/devolution/index.html) and Jerry Daniels’ Constellation (http://daniels-mara.com/constellation/). They are pretty different

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 15: Software at Speed of Sound -- Dan Shafer

xiii

products in many ways. If you find yourself uncomfortable in the Revolution IDE for some reason, you might want to check out those tools to see if they might do the job better for your style.

The programming language in the core engine is called Transcript. It is what is called a VHLL (Very High-Level Language). One day, I suppose, we’ll run out of superlatives and have to find other acronyms but for now, a VHLL like Transcript differs from a HLL like Java in that it stuffs more power into smaller spaces, sacrificing tiny bits of flexibility and control for awe-inspiring leaps of increase in ease of programming. (Clearly not every Java programmer on the planet would agree with that characterization. That’s OK. I’m still trying to figure out why anyone interested in truly rapid application development would adopt a language that spends most of its time looking for libraries on your hard disk. But that’s just me.)

Revolution and Other Languages I don’t think any intelligent programmer -- at least not one who programs for a living -- can get by with one tool in his or her toolkit. I don’t care how good the tool is. There are situations where the language you spend most of your time using just isn’t a good fit. In that case, you need another language or tool that is suited to the new task.

Choosing those other languages is a matter of knowing what kinds of applications you’re going to build and, therefore, where the weakness might be in your primary language. My primary tool is Revolution (just in case you forgot in the last few paragraphs). But if I need to do some browser-based, client-side work, I keep my JavaScript skills sharp as well, though I’ve become increasingly enamored of PHP in the past couple of years as well. Since I want to build Web-centric applications and useful personal desktop utilities, I don’t really need another language. I do maintain SQL capability, though, because at the end of the day the whole world’s a database and if you don’t know how to access it, you’re going to wish you did. And I dabble with Python, Smalltalk and RealBASIC because I’m an object-oriented guy at heart and Transcript isn’t yet object-oriented (though by the time you read this, the company may be releasing a new version that is).

You may be a corporate developer working in a Java shop. In that case, you might want to look at Revolution as a way to develop or support development of workgroup and department applications that would simply take too long to develop in Java.

In any case, try my rule for a while and see how it works. My rule is simple: develop it first in Revolution. If for some reason that’s not the right solution, you’ll at least have a decent spec for what to build in some other language.

Why I Wrote This Book This book grew out of the nagging of a small voice in my head, augmented by a few dozen voices on the Revolution users mailing list, who kept saying, “The online documentation that comes with this product is really good, but it’s reference material. I need something to help me really learn Revolution and, particularly, Transcript.” I knew I could write such a book because I’d done it before.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 16: Software at Speed of Sound -- Dan Shafer

xiv

Back in the days when Apple’s HyperCard was widely acknowledged (at least among Mac users) as a fantastic tool for building software to solve real-world problems, I was fortunate enough to stumble into it before it was released. I was immediately taken with it and then when I quite accidentally discovered there was a scripting language in it, I was so excited I think I bounced off walls for four days. I ended up writing the first book devoted solely to the HyperTalk scripting language (my buddy Danny Goodman, with Apple sanction and support, wrote the first HyperCard book and covered the language at a high level). My publisher, Howard W. Sams, and I worked like maniacs for about 3 months (aided greatly by my wife, Carolyn, and stepson Tomas Hernandez, who worked long nights creating the camera-ready copy) and we were able to release my first HyperTalk book at MacWorld Expo in San Francisco in January of 1989. It was an instantaneous success; I autographed hundreds of copies at the show and it went on to become a major best-seller as computer books go.

Revolution owes its roots to MetaCard, which in turn was intended to replicate much of the functionality of HyperCard on non-Apple platforms. More to the point, Revolution builds on HyperCard compatibly. So I figured I’d blow the dust off my old manuscript, make a few corrections, and get to market quickly with a book that would be helpful if incomplete.

I should have known better.

My perfectionist self kept making “one more tweak” and then adding “one more concept” and then writing “one new chapter.” The result is what promises to be a 1,500-page project that does at least attempt to capture all of the important aspects of Transcript programming and Revolution development. This is only fitting; Revolution is by my estimate 3-5 times richer, more robust and more powerful than HyperCard. It is also inevitably more complex, though I think Revolution’s developers have done a sterling job of keeping that complexity below the surface as much as possible.

I probed the user base for Revolution through the mailing list and other channels and determined that it would probably be possible for me to sell enough copies of this work in electronic form that I could at least feel fairly compensated for the time I took to produce it. Needing no further encouragement, I embarked on the project.

Early on, I determined that I had to release this beast in multiple volumes. Otherwise, it would be six to nine months before the project would be complete. I didn’t want to delay its introduction (or my cash flow) that long. So I carved it first into two volumes and then ultimately into three. This first one approaches 300 pages and I fully expected the others to be longer still.

As I neared completion of this first volume of the book, a number of old HyperCard colleagues jumped out of the woodwork. Among them was Ro Nagey, whom I remembered from the halcyon days of HyperCard when he ran Royal Software, one of

Andrew
Highlight
Page 17: Software at Speed of Sound -- Dan Shafer

xv

the biggest suppliers of HyperCard applications and tools. It turns out Ro was working with Runtime Revolution in a sort of quasi-official capacity as their evangelist. (He has since become an official part of the RunRev team.) He asked me if I’d be interested in a formal publishing relationship with the RunRev folks. We quickly agreed to the framework for a deal. I had never planned to publish these books in printed form other than print-on-demand, which would probably have been quite costly to you, the reader, so this working relationship with RunRev made great sense to me.

We mutually worked through the pains of starting a new publishing company in the midst of a software development company, produced the first edition of this work, and as its print run began to sell out, we decided a couple of things. First, it makes no sense for a software publishing company with no experience in the book business to try to be in the book publishing arena. It was too distracting and caused too many issues that the company had no particular experience solving. Second, a multi-volume printed series of books that would bite off huge chunks of functionality, fairly arbitrarily grouped, wasn’t going to meet the needs of most Revolution developers.

So we came up with a new plan. There will not be a Volume 2 and a Volume 3 of this series. Instead, I’ve been writing and releasing eBooklets focused on very specific topics, and releasing them as smaller, lower-cost products in electronic form only. Through my company, Shafer Media, we have released the first two of these SmartEBooks™, which are not only PDF files but also frequently include code samples and other materials in a single downloadable product that keeps itself updated as changes and new releases occur. These SmartEBooks are written in (surprise, surprise!) Revolution. Their intelligence is thanks to two important ideas.

First, because of the way Revolution is structured into stacks that comprise a single application, it is easy to embed binary content (like PDF files) directly into the stacks. This means that a file that I would normally be hard-pressed to automatically update for you is a piece of cake to handle because it doesn’t exist as a separate file. It’s just a property of a Revolution stack. When I change the file or one of the other components of a SmartEBook, I uploaded it once and the next time you open your copy, it checks my server to see if there’s a newer release. If so, it offers to download it for you. It’s up to you whether to do so then or wait until a more convenient time. (By the way, if you open the SmartEBook and you’re not connected to the Internet at the time, nothing bad happens.)

Second, thanks to some awesome technology developed by my good Revolution buddy Chipp Walters of Altuit Software called “MagicCarpet,” the automatic updating and file management (aka version control) of my SmartEBooks is all but transparent to me as an author and publisher.

So this book is now available as a SmartEBook (as you probably already know unless you bought it in printed form) which will constantly update itself as I fix typos, improve language, add examples, and otherwise make it better and better over time. But meanwhile you get a book that’s 90% or more of the way to what I’d want to release in

Andrew
Highlight
Andrew
Highlight
Page 18: Software at Speed of Sound -- Dan Shafer

xvi

final form without waiting for my perfectionist inner self to finally let go of it. Nice win-win, eh?

Who Should Read This Book? This book is aimed at people who want to learn to use Revolution’s built-in Transcript scripting language. I do not spend much time in this book teaching you how to use the IDE to create new stacks, add objects to your stacks, or deal with user interface “stuff.” My focus is on the scripting language itself.

That said, if you’ve worked through the excellent video tutorials in the Revolution product, you have enough background to understand and benefit from this book.

From response to the first edition of this book, it is clear that even relatively long-time users of Revolution learn at least a few new and helpful things from reading this book, but the audience I have clearly in mind as I write it is the relative newbie.

If you have some experience with HyperCard, SuperCard, PLUS, OracleCard, or, to a lesser extent, Assymetrix Toolbox, you will be able to quickly identify those things that are different in Revolution and Transcript and see how to apply those differences as you work on new products or convert old ones.

A word about Dreamcard before we proceed. In 2005, Runtime Revolution, in its continual process of re-examining its marketing and positioning, created a new product called Dreamcard. It made its first incarnation as the Dreamcard Player that shipped silently with each copy of Revolution sold in those early days. But the company also released Dreamcard as a separate product development tool. This lower-priced product is aimed at folks who don’t need to create standalone applications but who would be content with having their Dreamcard stacks usable by people who own the freely distributable Dreamcard Player instead.

If you create a stack in Dreamcard, you can make it available through our own channels, in your company or workgroup, in your school, and your users only need a copy of the free Dreamcard Player for the platform on which they wish to use the stack. Your Dreamcard stacks will run -- generally without any extensive modification -- in Dreamcard Players running on Macintosh Classic, Macintosh OS X, Windows (all modern flavors) and all the popular forms of *nix. The only substantive differences between Dreamcard and Revolution are: (1) Revolution creates traditional stand-alone applications that don’t require your users to own the Player; and (2) Dreamcard does not include certain professional add-ons, particularly database access tools.

What’s in This Book? This book begins with Ro Nagey’s witty and insightful Foreword, dubbed, in true Transcript style, on preOpenBook. In that chapter, Ro explains colorfully his affinity for Revolution, his feelings about me and this book, and gives you just a glimpse of the elegant and crisp syntax that makes Transcript the approachable language that it is.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 19: Software at Speed of Sound -- Dan Shafer

xvii

Chapter 1 is a quick walk-through of the IDE in the form of creating a small toy application. Its purpose is to cement the ideas of working with the IDE that you’ve encountered in working through the video tutorials while viewing those ideas in the context of creating an actual application.

Chapter 2 focuses on refreshing your memory about how to build stacks. It covers:

• how to create new stacks (main stacks and substacks)

• how to place new objects on cards contained by stacks

• how to group objects on a card so they appear on all new cards in a stack

• how to save a stack

• how to create and edit scripts attached to objects in stacks

Chapter 3 turns its attention to the individual building blocks of Revolution stacks and applications. In this chapter you learn:

• how to give Revolution objects more meaningful names

• how to arrange objects in a Revolution stack window

• how to change characteristics (properties) of Revolution objects

• the differences between running a stack in the IDE and as a standalone

• minimal menu creation

In Chapter 4, I discuss the basic building blocks that make up a Revolution application, providing an overview of the key categories of components and of all of the objects each comprises. That means it covers:

• action elements

• passive character traits • objects

Chapter 5 begins the real focus of this whole series of books: Transcript scripting. It covers the basic concepts of scripting, including:

• the mechanics of entering, editing, printing, and managing scripts

• handlers and functions their role in Transcript scripting

• messages and how they are passed inside Revolution

• variables and how they are referred to and manipulated

• the special concept of a container and how it is used

• addressing the components of a field in an English-like way

System messages are the throbbing heartbeat of Revolution and they take center stage in Chapter 6 where you’ll learn:

• what a system message is

• how to choose the destinations for system messages

• how to use all the system messages generated by Transcript

Page 20: Software at Speed of Sound -- Dan Shafer

xviii

• how to transfer messages from their default destinations to other target objects in the Revolution environment

In Chapter 7, I focus on more detailed exploration of the key system messages and commands dealing with the mouse and the keyboard. Specifically, you’ll learn how to:

• call and use several functions that help your scripts know where the mouse is and what it is doing

• simulate in a script the user clicking the mouse

• determine the status of the Command, Option, and Shift keys

• examine the keys being pressed by the user Chapter 8 explains the basic use of control structures such as repeat and if-then-else along with the logical operators (equals, less than, etc.) that play an integral role in their execution. You’ll learn:

• how control structures are used in Transcript scripts to execute groups of instructions repeatedly or conditionally

• what logical operators and related functions are available in Transcript

• how to use special Transcript commands to gain better control over loops

Managing cards and stacks through scripts is the focus of Chapter 9 where you’ll see how to:

• navigate among cards and stacks

• find and mark cards in a stack based on their contents

• manage multiple open stacks and their associated windows

• enable the user to return quickly to some predetermined point in a stackprint one or more cards in a stack, one card per page

Revolution can be used in database applications of a wide variety of complexity. In Chapter 10, I focus on the simplest aspects of database application development, those where you manage text and data without a formal database design or connection. You’ll learn how to:

• read the contents of fields

• get information about data in fields

• find and select field contents, including advanced search using matchText • modify the contents of fields

• work with styled (rich-formatted) text

• sort cards by the contents of fields

• use text in fields to create hypertext applications

• deal with date and time data as a special class of information • trap the use of the Return and Enter keys in a field

• access and use external text files as “poor person’s databases” Dialog boxes are the focus of Chapter 11, where you’ll learn how to use:

Page 21: Software at Speed of Sound -- Dan Shafer

xix

• answer, for dialog boxes requiring only that the user press a button to give your script some information

• ask, for dialog boxes requiring that the user type something to give your script some information

• answer file and ask file, for dialog boxes that help the user find or name external files and stacks

• ask printer, a dialog box from which the user can choose a printer and setup options

• ask password, for a special type of ask dialog box In Chapter 12, I take a look at menu design and implementation as well as the basic steps involved in creating your Revolution applications for distribution as stand-alone programs. This chapter teaches you

• a good strategy for scripting user interaction through menus

• how to attach scripts to menu items

• the platform-specific issues involving the use of menus

• basic steps to preparing a Revolution stack for distribution as a standalone application

Finally, for a parting shot, in the Afterword (or “on postReadBook”), I take you step by detailed step through the creation of a reasonably realistic real-world application, a To Do list manager that uses an external text file for data storage and which implements many of the features you’d want in a polished application. The program isn’t 100% complete (we’ll work on it in future volumes and extend it in some terribly interesting directions) but it’s usable. More importantly, it demonstrates the practical application of many of the ideas in the first 12 chapters of this book to a real-world problem and its solution.

OK, enough talk already. Let’s start the Revolution!

Page 22: Software at Speed of Sound -- Dan Shafer

xx

On preReadBook

A Few Words on Dan Shafer and the Making of Two Rev olutions

When I started college, I took my first computer programming course. FORTRAN. This was in the 60’s. Programming was done by waiting in line for a Hollerith machine to become available. Yep, back in the days of punched cards: one statement per card. Once you had your cards typed, you’d flip through them. Then, you’d put them in a box with other programs. Occasionally, you’d get an announcement over the PA system that the box with your cards had been dropped. Everything had to be resorted or retyped.

In an hour or so, your program would be run. Made a mistake? You had to repeat the process. You could spend all day and night in the cold, sterile room with poor fluorescent lights. Twinkies and coke kept you going.

I hated every minute of it.

Not only the endless amount of time spent just waiting – but we were given these ‘budgets’ for computer time. We didn’t have to pay for the account, but if you went over the budget allowed you, your grade went down.

One day, I passed a room that had these strange typewriter-like machines. Teletypes. I asked what they were for. “Oh, those connect you to the machine directly – expensive as heck, but you get immediate results.”

Hmmmmmm. I sat down and risked writing a tic-tac-toe program in FORTRAN. It worked: I discovered I had an ‘ear’ for programming – even though I hated it. Languages just weren’t adequate for me. Awkward. Good at one thing, horrid at something else.

However, I also hated a lot of the engineering courses I was taking. I’d make deals with my professors: I’d program for them if they’d let me out of lab courses or whatever. It was a mutually beneficial deal.

But, I hated programming. Good at it – but just had the feeling that I was serving the computer and not the way it ought to be: the computer should be serving me.

Flash forward several years: I get a call from Nick Griffin, CEO of Griffin Communications. He has this strange program he wants me to look at. Working on his blazingly fast Mac SE, he tells me he got it from a friend. It’s something new, not released yet.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 23: Software at Speed of Sound -- Dan Shafer

xxi

It was called HyperCard. Nick had this huge dining room. I sat at the dining table. Click buttons. Doing all the tricks we used back then. Option-click. Apple-click. Apple-Option-Click. Up pops a script. I won’t repeat the exact phrase, but as I looked at the script, I exclaimed, “Holy @$%^!”. My voice echoed in the cavernous room. It was a programming language I had never seen before but instantly understood. I’d find another script. “Holy @$%^!” And, then I changed an icon. “Holy @$%^!” Nick would ask, “Um, what is so exciting?” I said, “Just look! Holy @$%^!”. Playing with the Home stack that night, I wrote my first HyperCard program. No documentation. But, you could just look at the language and grok it. The UI was intuitive. Nick finally gave up, went to bed, telling me to lock the door on my way out. I was there for hours.

It became an addiction. If I could imagine it, I could write it. The trouble was: there was so much to the language and Apple’s documentation was fairly opaque. As the years would prove, Apple never really understood what it had.

Fortunately, there were people like Dan Shafer around.

Dan Shafer was one of the best-known and most popular writers and speakers in the early days of user-friendly software development tools. He was the author of the first and best-selling book, HyperTalk Programming, which sold out numerous printings and was widely viewed as the best book on the subject for more than two years after its publication, which coincided with Apple Computer’s public unveiling of HyperCard. Over the next few years, Dan wrote a half-dozen additional HyperTalk titles, developed Dan Shafer’s ScriptExpert, one of the most highly acclaimed HyperCard products ever produced, and spoke all over the world about the hot craze caused by HyperCard.

Without Dan’s book – and, to be fair, Danny Goodman’s and Revolution’s own Jeanne DeVoto’s titles as well – I would have missed 80% of the power contained in HyperCard.

It’s strange to say a programming language can change your life. This one did. I used it in all my work. It was quicker for me to write it than to wait for the IT department to do it; cheaper to write it than to buy something that re-imposed the tyranny of serving the computer or the software instead of them serving me.

Flash forward. Heizer Software was being sold. I had purchased virtually every programming tool they made. Al Kelley and I purchased it and renamed the company “Royal Software”. Our first product of note was LiveCard, allowing you to run HyperCard over the web to any computer on any platform. It was great.

But, HyperCard wasn’t multi-user and it wasn’t available on other platforms.

Millions of people discovered HyperCard lurking on their Macintoshes. Tens of thousands of them found the HyperTalk scripting language to be accessible and yet powerful. “How many of you started out dabbling in scripting, making little changes

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 24: Software at Speed of Sound -- Dan Shafer

xxii

here and there to other peoples’ scripts, and then suddenly found yourself surrounded by Jolt and Twinkies saying, ‘I guess I are a programmer’?” Dan asked at conference after conference, user group meeting after user group meeting. Thousands of heads bobbed up and down. It was clear that HyperCard had started a revolution and that Dan was one of its early leaders.

Apple Computer and later Claris Corporation executives charged with figuring out how to market HyperCard consulted with Dan frequently. Evangelists Guy Kawasaki and Robert Perez, along with Apple VP Jean-Louis Gassee invited Dan to strategic meetings about the future of HyperCard.

Over time, Apple fumbled the HyperCard ball. “They never really understood what they had,” Dan contends, “and since they couldn’t figure out how to make money on it directly, they ditched it. Bad mistake.”

How bad of a mistake? Well, Gil Amelio once said, in effect, that if Apple hadn’t dropped the ball on HyperCard then the World Wide Web would have been based on HyperCard. Big, bad mistake.

Then, under a rather onerous non-disclosure agreement, Param Singh, HyperCard’s product manager and the single most honorable man I’ve met in the software business, tells us about HyperCard 3. Thank God! HyperCard definitely needed upgrading. And HyperCard 3 was going to KILL.

Unfortunately, HyperCard 3 was killed. And thus began a long decline. Along with many others, I continued to use HyperCard. But, with faster machines and the demand for color and more modern UI’s, HyperCard began to die on the vine.

With HyperCard sent off to the software graveyard, other companies worked on similar programs. When Format Software gMBh in Koln, Germany, wanted to document their variation on HyperCard, a product called PLUS, they turned to Dan. When Oracle acquired PLUS and turned it ultimately into OracleCard, they called on Dan to develop sample applications, management consoles, and documentation. When Oracle had a big client interested in using Oracle with HyperCard, they’d hire Dan to come in and clinch the deal.

Software became boring again. More and more applications imposed their tyranny on the user. “Do it our way or don’t do it at all”. Languages were ugly again. They were complicated, non-intuitive and, well, just not fun. Who wanted to write creative software anymore? Who could afford to invest the time or raise the energy to conquer what were passing for advanced computer languages?

They were all computer-centric languages. They were platform-centric. They were, well, just plain pug ugly.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 25: Software at Speed of Sound -- Dan Shafer

xxiii

Again, Dan Shafer kept the flame alive.

Dan created the concept of the Inventive User, a term and concept since used by dozens of companies to explain their technologies and their users’ needs. “An Inventive User,” Dan said, “is someone who is smart enough to develop software his team or work group needs but who isn’t a full-time professional programmer and probably lacks recent formal computer training. It’s someone who knows they should be able to get more out of their computer than they’re getting but for whom the corporate IT department just doesn’t have the time or resources.” Publishing The Inventive User Letter, Dan became known as the champion of the scripter community

Meanwhile, Dan’s career continued on the Inventive User trajectory as he undertook documenting UserLand’s Frontier product and continued his career as an author (he now has more than 60 books to his credit).

For years, I kept using HyperCard as my programming topol of choice: years beyond the life cycle of any normal product that has been discontinued. Why? Quite simply, I could write a HyperTalk program in minutes that would require days in another language. It was astounding to me when I looked at other languages: Why couldn’t they learn from HyperCard and put the user back in control?

I surrendered: the only way for this power to become available again would require a revolution. The big companies simply enjoyed their comfortable positions too much to make a programming language and environment that was easy-to-use, self-documenting, cross-platform and economical.

Fortunately, for me – and you – the revolution began in 2000 by a handful of enterprising Scots who created a company named Runtime Revolution. Their name says it all…and in less than 3 years have created a tool that makes programming fun again.

Now, the Revolution has begun. Cross-platform, English-like, blazing fast, intuitive: we use the term: User-centric Development.

Fortunately for all of us, when Dan became aware of Runtime Revolution, he downloaded a trial version and was immediately re-hooked. “I’m back among the revolutionaries,” he wrote to the user group mailing list. He described Revolution as “HyperCard on steroids and amphetamines running on multiple platforms.”

He initially decided to try to make a quick-and-dirty release of his old HyperTalk best-seller work for Revolution users, more as a labor of love than a revenue source. But the project quickly grew and took on a life of its own.

Andrew
Highlight
Andrew
Highlight
Page 26: Software at Speed of Sound -- Dan Shafer

xxiv

As Runtime Revolution’s Evangelist, it’s been my distinct privilege to work with Dan. His enthusiasm, wisdom and patience made my job a pleasure. We’ve had a lot of fun reminiscing about the ‘old’ days – and marveled at how exciting the future has become with Revolution’s power and elegance. And, due to the acquisition of the engine technology behind Revolution, Runtime Revolution has released new, lower-priced versions that are sweeping the user community again. There’s a rumble being heard as more Inventive Users, students and professional programmers join the new revolution. It’s going to be good.

“Programming is fun again,” Dan says as he heads back into his home office in beautiful Monterey, California, where he and his wife, singer-songwriter Carolyn Shafer, live. with their Shiitzu, Einstein.

Ro Nagey

Runtime Revolution, Ltd.

September 21, 2003

Chapter 1: Wrappin’ My Head Around the Revolution

I confess. I’m a programming language junkie. I’ve learned and explored more programming languages in a career during which I’ve seldom been paid only to write code than most programmers have even heard of. Over the years of poking at so many syntax-laden beasties, I’ve adopted a get-acquainted strategy that has served me quite well. Whenever I start with a new programming language or environment, I first create what has actually become quite well known as the Dan Shafer Counter Tutorial.

In this chapter, I’m going to let you look over my shoulder while I build that tutorial.

I’m going to go through this project relatively quickly. I want to convey to you some of the flavor of this environment and of the Transcript programming language as well as to share with you some of the excitement I experience when I get to develop software in what has become my favorite tool. I’m not going to explain in detail how everything I do works, or why I do some of the things I do. My purpose here is not to provide you with a detailed tutorial introduction to Revolution.

You’re welcome to work alongside me here. Hang on while I shove some of my notes and papers and old Three Musketeers™ wrappers out of the way. There. OK, let’s go diving into the Revolution.

Before we begin, a word about program names. Runtime Revolution Ltd. publishes two programs that are for many purposes identical: Revolution and Dreamcard. For 90% of the material in this book, you don’t need to worry about the differences. I’ll use the word “Revolution” because it’s been around longer, is more familiar to most people using the software, and is ingrained in my thinking

Andrew
Highlight
Andrew
Highlight
Page 27: Software at Speed of Sound -- Dan Shafer

2

and writing pattern. Where there are differences between the two, I’ll clearly point them out.

Designing the Counter Tutorial One of the reasons I use the Counter Tutorial as my first introduction to a new environment is that it doesn’t take a lot of design. I am impatient. I want to roll up my sleeves and get started. I don’t want to dork around with a bunch of class diagrams and object hierarchies. I’ll save that stuff for later when I’m building a real app; for now, I’m just playing around here.

Figure 1-1 shows what the finished Counter Tutorial’s only window looks like. I bet you can figure out from looking at it what I expect it to do, right?

Figure 1-1. Counter Tutorial Screen Design

This counter just allows the user to increment the big number by 1 or decrement it by 1 or reset its value to zero. Nothing more sophisticated than that. The “+” button increments, the “-“ button decrements and the “Reset” button resets.

OK, so we need three buttons and a text field and no menus for this simple little demonstration application.

Getting Started I’m going to assume you have Revolution installed on your machine. If not, you’ll need to do that if you’re following along. Instructions for installation come with the downloaded product. (The free trial version of either Dreamcard or Revolution will work for this tutorial and, indeed, for the entire book.)

Go ahead and install it. I’ve got another Three Muskeeteers around here somewhere I can munch while I wait.

Once I’ve installed Revolution on my system, I’m going to click on the Documentation link in the little toolbar at the top of the screen. (Note that you may see the revOnline viewer pop up automatically when Revolution/Dreamcard opens. This is a preferences

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 28: Software at Speed of Sound -- Dan Shafer

3

setting you can turn off if you don’t want to see this every time you begin to work in the program.)

I peruse that window (see Figure 1-2) and I figure it might be a good idea to start with the “Recommended Resources for Getting Started.” So I click on that link and up comes a list of tutorials like the one shown in Figure 1-3.

Figure 1-2. Main Revolution Documentation Window

Page 29: Software at Speed of Sound -- Dan Shafer

4

Figure 1-3. Tutorial Page of Revolution Documentation

Well, there’s a tutorial called “Getting Started,” so I’m going to begin by going through it. You might be expecting to spend the next several hours reading some painstakingly written interactive tutorial. Nope. Revolution ships with a connection to a series of well-done video tutorials. So when you click on the “Getting Started” link in the window in Figure 1-3, the revOnline viewer (see Figure 1-4) opens. You can now click on the thumbnail image of the video. The movie will download over the Internet the first time you use it. Later, it may be cached on your local machine.

Note that you can also download and print out an Acrobat PDF document that is essentially a transcription of the video’s narration. You might find it helpful to have this in hand as you view the video if you’re the kind of person who learns better from reading than observing.

Note, too, that if you purchased Revolution on a CD-ROM through a store or from Runtime Revolution directly, this and other tutorial videos are on that CD and can be viewed without an Internet connection.

Finally, you’ll notice that there are quite a number of training videos in the rest of the revOnline viewer that you may not own the rights to view. You can always purchase those by clicking on the appropriate links in the viewer when you need them.

Page 30: Software at Speed of Sound -- Dan Shafer

5

Figure 1-4. revOnline Viewer Page for Downloading and Viewing “Getting Started” Tutorial Video

(As you go along in Revolution, you’ll discover as I did that this program has some of the finest online documentation of any program you’ll use on your system, courtesy of documentation diva Jeanne DeVoto and a number of people who have followed her into that task. It really is pretty awesome. And it’s all searchable and cross-referenced eight ways from Sunday, too. This doesn’t mean it’s perfect. But my experience -- echoed by that of thousands of other users -- is that you can almost always find what you want even if it takes a little digging.)

I recommend you go through the first four video tutorials, which will take you less than an hour. By the time you’ve gone through those tutorials, you’ll have a pretty good handle on how Revolution works and how it thinks about things in terms of stacks (mainstacks and substacks) and other objects. Now I just go to the File menu and tell Revolution to give me a new Mainstack. (But don’t let my impatience cheat you. Make a note to go back and work through the other videos at some point. I have.)

Page 31: Software at Speed of Sound -- Dan Shafer

6

Voila! Up pops a nice clean window where it looks like I can lay out my application’s one and only window.

The layout for my application is pretty simple, but this window’s far too big, so I’ll begin by reducing its size. Simple: drag the lower right corner up and to the left until I get to a size I think will work. I can always change it later. For now, I end up with a window that looks like Figure 1-5.

Figure 1-5. Resized Initial Mainstack Window

Creating the Field for the Counter’s Value OK, let’s lay out the window. Let’s start with the text object in the middle of the screen where the counter’s value is shown. This ought to be easy. There’s the tool palette floating off to the left there, and the icons are pretty self-explanatory. Pausing the cursor over the various types of fields I could create, I decide that what I need here is a plain text entry field as shown from the menu in Figure 1-6. (It turns out I could also have chosen a Label Field, but that isn’t so obvious at this stage.)

Page 32: Software at Speed of Sound -- Dan Shafer

7

Figure 1-6. Tool Palette With Field Tool Selected

There are two ways to add a control from this palette to a stack window. You can either double-click the tool’s icon or you can click and drag the control into the window. If you double-click it, Revolution will position the new control in the center of your stack window. If you drag the new control into your window, of course, you can place it where you like. In either case, the result of placing a text entry field control in our new window looks more or less like Figure 1-7.

Page 33: Software at Speed of Sound -- Dan Shafer

8

Figure 1-7. Initial Placement of Text Entry Field

Pretty cool, eh? I mean, it’s not rocket science but I’m laying out this window and so far, there’s nothing tricky going on.

This field is obviously too small and not properly positioned for the layout I’ve got in mind, so we need to manipulate it a bit. When you create a new control in Revolution, it is left selected. To move it, just click anywhere inside of its boundaries but not on any of its borders and then drag it where you want it. To resize or reshape it, click and drag one of the resize “handles” dotting the perimeter of the new control. Figure 1-8 shows how this field looks when you finish placing and sizing it to more or less match the layout in Figure 1-1.

Page 34: Software at Speed of Sound -- Dan Shafer

9

Figure 1-8. Text Entry Field Resized and Re-Positioned

Let’s see, I think I’ll try first to get this field to display the number in large type. I click on the browse tool in the tool palette (the pointing hand in the upper left corner) so that I’m not editing my stack any more, but rather running it. (This seamless integration of the runtime environment and the editing environment is one of the things I love most about Revolution. It beats the heck out of Java’s edit-compile-link-test-edit endless cycle. I’m an instant-gratification kind of guy!)

Now I type the number zero into the field. Oops. Not quite what I had in mind. (See Figure 1-9.)

Andrew
Highlight
Page 35: Software at Speed of Sound -- Dan Shafer

10

Figure 1-9. Type Too Small in Counter’s Value Field

Let’s see. What’s the best way to adjust the appearance of the text in a field? The Text menu holds promise. So I go there to check it out. Looks like it’s what I need. But here I encounter one of those characteristics of Revolution that is different from most other applications I use. I find that if I type some text into this field (after first figuring out I have to choose the Browse tool to do so) and then select that text and apply formatting, the result isn’t what I bargained for. The character I type disappears almost entirely off the top of the field or does other strange things. This happens because I’ve forgotten or misunderstood a key idea in Revolution. If I want a component -- in this case a Text Entry Field -- to behave differently from the way it is set up by default, I have to apply my changes to it, not to its contents. (This is a bit of an over-simplification but for now it’s an appropriate one.)

Making sure this field has no text in it, I select it using the Edit tool and then go to the Text menu to set up the formatting I want to apply to whatever text gets placed into the field. So I pick Bold and make the type as big as the menu easily allows, 48 point. While I’m at it, I set the alignment of the text to center in the field. The result looks like Figure 1-10.

Figure 1-10. Text in Counter’s Value Field Properly Displayed

Saving for the First Time This seems like a good time to save my work. But what exactly am I saving? I know I have a mainstack (because that’s what Revolution had me create at the beginning of this exploratory session), but I’m not so sure how that relates to how this stack gets saved to my hards drive. In Revolution, as it turns out, the mainstack gets saved in something called a “stack file” that contains one mainstack and zero or more related substacks. So I’m betting that I need to save the file, not just the mainstack.

Pull down the File menu, pick the “Save” option, and figure out where I want to place this file, then save it as “Counter Demo.rev.” Great. But the mainstack is still called “Untitled 1,” which as at the very least untidy. (Untitled...untidy...get it? Ahem. Moving right along.)

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 36: Software at Speed of Sound -- Dan Shafer

11

So how do I change the name of the stack? I scan the menus (one of the handiest things to do when learning a new environment of any kind, of course), and I see that on the Object menu, there’s an item called “Stack Inspector.” That sounds promising so I try it. A window like the one in Figure 1-11 appears.

Page 37: Software at Speed of Sound -- Dan Shafer

12

Figure 1-11. Stack Inspector Opened On My Mainstack

Obviously, I can change the stack’s name right here. So I do so. I call the stack, logically enough, “Counter.” Then I save the file again.

Page 38: Software at Speed of Sound -- Dan Shafer

13

(Briefly, I wonder about the difference between the stack’s name and its title, but since what I did had the desired effect, I decide to leave that subject for further exploration down the road.)

Button Time I’m ready now to place the first button on my Counter demo stack. I click on the rectangular button tool (third row, left tool on the tool palette) and drag a button control to the left of the field in my mainstack window. After resizing it and reshaping it, the screen looks something like Figure 1-12.

Figure 1-12. First Button Placed on My Mainstack Window

It appears to me as I think a little about this button that I need to do three things to it

1. I need to change its name to “-“.

2. I need to make its name a lot bigger.

3. I need to program it to do something useful (namely, subtract 1 from the counter’s displayed value when it’s clicked).

When I needed to change the stack’s name, I found that the Object menu had a “Stack Inspector” menu item. If I leave the button selected and go to the Object menu, will I find a “Button Inspector” menu item? Well, not quite, but I do find a menu item called “Object Inspector.” But as I’m pulling down to that menu, my eye catches the open Stack Inspector and, what do you know? It has changed its name to “button.” Hmmm. What if I click on the field I created earlier? What do you know! The inspector window changes so I’m working with the field now. So it appears that the inspector window updates itself to reflect the object I’ve selected in my layout. This is obviously going to give me a very fast way to modify objects I’ve placed on the window layout.

Page 39: Software at Speed of Sound -- Dan Shafer

14

I’m ready to rename the button and set up the text size and alignment for its label. I go to the Text menu as before and set up a 48-point, bold, centered label for the button. In its Basic Properties inspector, I change its name to “-“. The result is shown in Figure 1-13.

Figure 1-13. First Button Placed and Labeled on My Mainstack Layout

I save my work again and then I stop for a moment to think about the best way to proceed. I’m going to have three buttons in this window. Two of them are going to be identical except for the arithmetic they perform, which ought to be a minor change in their scripts. The Reset button differs a bit, but not by so much as to be radically different.

I can take two different approaches here. The first would be to create all three buttons, then go script them individually. The second would be to script this button, test it, and then just copy the button, paste it where I want it, and tweak the script to do the different math. This second approach appeals to my basic lazy nature, so I decide to see if it will work. (If not, it won’t be the first blind alley I’ve visited in my days of programming.)

So the first thing I’ll do is script this button. How do I get at the script? Well, it turns out there are several ways of doing it. I’m going to use one of the most direct. Making sure the button is selected, I go to the Object menu (which is fast becoming our bosom buddy) and, sure enough, there’s an entry there for Object Script (see Figure 1-14). I note that it has a Command-E keyboard equivalent and I tuck that knowledge away for possible future use.

Page 40: Software at Speed of Sound -- Dan Shafer

15

Figure 1-14. Opening an Object’s Script from the Object Menu

I open the script editor for the button and I see a nearly blank editing window like the one shown in Figure 1-15. This won’t be a surprise if you completed even the first of the video tutorials.

Page 41: Software at Speed of Sound -- Dan Shafer

16

Figure 1-15. Blank Script Editing Window

Hmmmm. OK, what do I know about scripting from what I learned in the Getting Started tutorial?

We learned that a script has a name that defines when its contents are executed.. We met the handler message called “on mouseUp,” which gets triggered when the user clicks the mouse over an object. Interestingly, Revolution has supplied the opening and closing lines of a mouseUp handler for us when we open the script editor window. It does this because, well over 90% of the time, when you create a new button and open its script, you want to create a mouseUp handler so the button can respond to mouse-clicks.

So what do I want to have happen when the user clicks on this button? I want the number displayed in the field to have its value decreased by one. In other words, I want to subtract 1 from its value. This means, among other things, that I need to be able to call the field by name so my script will know what I’m telling it to look at and act on. When I created the field, it was given the meaningless name “field” and although there’s no technical reason I can’t use that name, it seems kind of tacky. I could close the button’s script editor window right now and go give the field a name, but there’s no need to do that. Just to prove how flexible Revolution is, I’m going to write the script as if I’d already named the field counterValue and then change it when I’m done with my script. So now I wonder how I can subtract something from this field.

I open the Documentation (assuming I haven’t left it open), and then click the tab for the Transcript Dictionary to see if I can find anything that looks promising as a way to do this. I figure I’ll search for the command “subtract.” Knowing programming languages, of course, I don’t expect this to turn up anything immediately useful, but maybe it will provide me with a pointer.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 42: Software at Speed of Sound -- Dan Shafer

17

What to my surprise should happen? Up pops a dictionary entry for the word “subtract.” Wow. This is pretty sweet. I read the documentation (see Figure 1-16) and I can see that I just have to tell Revolution to “subtract 1 from” something. I’m going to guess that I need to subtract 1 from whatever is in the field. So I’ll try the obvious first, typing in the one-line script like the one shown in Figure 1-17.

Figure 1-16. Documentation for subtract Command in Revolution

Andrew
Highlight
Andrew
Highlight
Page 43: Software at Speed of Sound -- Dan Shafer

18

Figure 1-17. Completed Initial Script for Button in My Mainstack

After clicking the “Apply” button to assign the script to the button, I close the script editor. Remembering that I have told the script that the field is called counterValue , I select the field in the layout, go to the Property Inspector, and change the field’s name to reflect the script. Then I save my work again (I’m paranoid about such things).

I put Revolution into browse mode so the stack will act like a program instead of allowing me to edit its contents. I click on the minus button.

You gotta be kidding me! My code never works the first time. But as you can see from Figure 1-18, this just works as expected. Sweet.

Andrew
Highlight
Andrew
Highlight
Page 44: Software at Speed of Sound -- Dan Shafer

19

Figure 1-18. The Button Works the First Time!

Copy, Paste, and Tweak? Is That All There Is?

OK, let’s see if we can use copy-and-paste-and-tweak methods to get the “+” button working even more easily than we did the “-“ button.

I’m going to select the “-“ button and then explore the Edit menu to see if I can find a copy routine. Hmmm. “Duplicate” sounds promising; it should combine a copy and a paste in one step. Let’s give it a shot. Yep, the result looks like Figure 1-19.

Andrew
Highlight
Page 45: Software at Speed of Sound -- Dan Shafer

20

Figure 1-19. Minus Button Duplicated in My Mainstack Layout

I drag the button into position to the right of the field where the value is being displayed and I line it up as closely as I can with the minus button to the left. Then I go to the Object menu again, choose “Object Inspector” and change the button’s name to “+”. The result looks like Figure 1-20.

Figure 1-20. “+” Button Placed and Renamed

Now I have to modify the script of this button so it adds 1 rather than subtracting 1. I check the documentation again and, sure enough, there’s an “add” command, so I just edit the script of this button so it looks like Figure 1-21, click the “Apply” button, close the script editor, and save my work again. Then I test it again. And again, it just works. (I’m going to stop being surprised by this any minute now, I promise.)

Page 46: Software at Speed of Sound -- Dan Shafer

21

Figure 1-21. Script for “+” Button After Tweaking

Creating and Scripting the Reset Button We have just the Reset button left to do. I create a rectangular button about the same width as the field and place it immediately below it on the layout. I open the Object Inspector from the Object menu and change its name to “Reset.” Then I set the font size to 48. Oops. The result (see Figure 1-22) is not quite what I had in mind. Obviously, I need to set the type a bit smaller. I experiment and I find that a size of 24-point works quite nicely, so I set it there.

Figure 1-22. Reset Button, With Type Set too Large

Andrew
Highlight
Page 47: Software at Speed of Sound -- Dan Shafer

22

Now we just have to figure out how to script this button so it replaces whatever value is in the field counterValue when it’s clicked with a “0”.

Here’s where my HyperTalk experience gives me a slight leg up. It might not be very obvious to you, but you change the values of things in Transcript by using the put command. You can put some value into some field or other container. (A container is just a Revolution object that’s capable of holding a number or some text. For this discussion, that just means fields, although I can assure you that it is a bit more complicated than that.)

So the script that will put a value of “0” into the field would look like this:

put “0” into field “counterValue”

This should be a one-line handler for the on mouseUp event, so the resulting script for this window should look like Figure 1-23.

Figure 1-23. Script for Reset Button

NOTE: You may be wondering why I put the zero in quotation marks. If you’ve done some programming, in fact, you may think this is an error. After all, if I want to add 1 to and subtract 1 from some value, that value has to be a number and not a string, right? Well, as it turns out in this case, I could use either the number 0 with no quotation marks or the string “0” in quotation marks. The result is the same. The question of when you must put quotation marks around literals is beyond the scope of this chapter. As a matter of programming style, I generally put quotation marks around any literal string to differentiate it from other symbols with different meaning.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 48: Software at Speed of Sound -- Dan Shafer

23

Apply the script, save the stack, and test it out. Everything is now working as originally planned.

Where Do We Go From Here? Now that we’ve completed a simple tutorial application in Revolution, there are two questions that remain to be answered. Both questions begin with the single issue of what steps are next.

First, where do we go with the tool, with this application and with others we want to create with Revolution?

If you own Dreamcard, your next step is to make your application file -- whether it contains a single mainstack file like our Counter Demo or multiple stacks as it might in a more complex application -- available to people who own the Dreamcard Player. The free Player is available for most major operating systems (Mac Classic and OS X, all flavors of Windows, most flavors of Linux). You can, of course, distribute the Dreamcard Player to others along with your stacks (which are sometimes called “stackware” to differentiate them from software, which doesn’t usually require a player).

One way to make such stackware available is through RevOnline. You can create a user space in RevOnline right in the Dreamcard development environment, upload your stacks to that area, and then notify prospective users, customers, and others of their availability.

You are not, of course, limited to RevOnline for distributing your stackware but it’s a good place to begin even if you plan to supplement it with your own online store and other channels of distribution.

If you own a license to Revolution, then you can create true standalone applications which your users can download and run without owning the Dreamcard Player or any other software. In other words, your applications are full-fledged “double-clickable” applications in the same sense as any software from Apple, Microsoft, Adobe Systems, Macromedia, and hundreds of other software companies. Depending on what license you own, you can compile applications for one or more “target” machines.

The other sense in which the “Where do I go from here?” question comes up is in terms of learning Revolution. There are dozens, perhaps hundreds, of great resources available. The other chapters in this book are designed to take you slowly and gently through the process of learning all the basics of Revolution and Dreamcard. I also publish other eBooks and eBooklets fairly regularly that focus on specific topic areas that Revolution users express interest in. At this writing, two such eBooklets are available, one on using Revolution as a Web server CGI and the other on the use and nuances of custom properties. Another on printing is imminent.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 49: Software at Speed of Sound -- Dan Shafer

24

All of my eBooks and eBooklets are available through my online store at http://shafermediastore.com. They generally sell for $10 each or less. You can register at my store to receive email notification when I release a new title. This book is also available in printed form, again through my online store. (You are, of course, free to print it yourself or to print selected portions if you like.)

Don’t forget the other video training products available from Runtime Revolution through the online documentation and RevOnline. And by all means join the user list for Revolution, even if you’re a Dreamcard user. The list has a bunch of incredibly knowledgeable, friendly and immensely helpful people who answer questions with amazing speed and depth. I owe a lot of what I’ve learned about the nuances of Revolution to the members of that list.

Welcome to the Revolution!

Page 50: Software at Speed of Sound -- Dan Shafer

25

Chapter 2: Basics of Stack Building In this chapter, you’ll refresh your memory about:

• how to create new stacks (main stacks and substacks)

• how to place new objects on cards contained by stacks

• how to group objects on a card so they appear on all new cards in a stack

• how to save a stack

• how to create and edit scripts attached to objects in stacks

I’m going to go through this stuff pretty fast. I assume you have some basic familiarity with Revolution. If you don’t, I suggest that you work your way through the excellent tutorials that come with Revolution. Then come back here for a quick refresher.

In the rest of this chapter, I’m going to walk you through the creation of a very simple but semi-functional note-taking stack. In Chapter 3, we’ll do more to flesh it out and give it some spit-and-polish, but in this chapter, you’ll get to see the basic stack-creation process up close.

NOTE: The Revolution IDE is a bit of a moving target. As the company releases new versions of its products, it not-infrequently changes the way something in the development environment looks or behaves. So if you find that from time to time what you’re seeing on your screen doesn’t quite match what’s in the figures in this book, don’t panic. You’re probably just working with a later version of the program. Basic behavior doesn’t change much, so even though appearance may look out of synch, you’ll be fine.

Creating Stacks When you open Revolution fresh, you will probably see a few things lying around in the IDE:

• a tool palette (see Figure 2-1)

• a toolbar (see Figure 2-2)

• an empty application browser (see Figure 2-3)

• possibly a message box (see Figure 2-4)

(If you don’t see the message box and you want to, just press Control-M or click its icon or text link in the toolbar to bring it into view.)

Page 51: Software at Speed of Sound -- Dan Shafer

26

Figure 2-1. Tool Palette in Revolution IDE

Figure 2-2. Revolution Default Toolbar

Figure 2-3. Application Browser

Page 52: Software at Speed of Sound -- Dan Shafer

27

Figure 2-4. Message Box

As you know, every Revolution application consists of at least one stack, called a mainstack. In a case where you haven’t yet created any stacks in a Revolution development session, you can create a new mainstack by choosing “New Mainstack” from the File menu. Revolution creates a new mainstack like the one shown in Figure 2-5.

Figure 2-5. New mainstack

Note that although Revolution will let you go on creating mainstacks all night, each mainstack must ultimately become the only mainstack in a Revolution stack file, which consists of one mainstack and zero or more substacks associated with that mainstack.

Putting Stuff Into the Stack OK, so now you have a mainstack. What can you put into a stack window and how do you do it?

Revolution defines numerous types of objects that you can place in a window. These objects are referred to collectively as “controls.” Although it is a bit of an oversimplification, you can think of these controls as falling into two broad categories: display controls and active controls. In general, active controls change the contents and appearance of display controls; a good part of an application consists of interactions among controls of these two basic types.

I’m going to focus for now on three controls:

• buttons

• fields

• label fields

Page 53: Software at Speed of Sound -- Dan Shafer

28

(Actually, I’m cheating a bit. Fields and label fields are just variations on one type of object Revolution knows about. That’s OK, though. By the time we’re done with this book you’ll know about a couple dozen such objects, many of which are variations on other themes.)

Buttons are active controls; fields and label fields are display controls.

There are at least two ways to put any object into a stack window. Which you adopt is a matter of personal taste.

The first way is to use the tool palette. Let’s use that one to add a button to the new mainstack window we just created. There are two ways to do this, as we saw in Chapter 1. You can either double-click the button icon and have Revolution place a button in the center of the active window, or you can drag a button from the tool palette to the desired location in the window. The button icon is the second from the top in the left column of the palette, shown in Figure 2-6.

Figure 2-6. The Tool Palette. Button Icon is Second From Top, Left Column

I tend to use drag-and-drop for component placement. Drag the button from the tool palette to a position near the bottom of the window, more or less centered in the width of the mainstack window and release the mouse. The result should resemble Figure 2-7. Don’t worry about precise positioning.

Andrew
Highlight
Andrew
Highlight
Page 54: Software at Speed of Sound -- Dan Shafer

29

Figure 2-7. Creating a New Button in a Mainstack Window

OK, now let’s look at the other approach to placing new objects in a mainstack window. Go to the Objects menu and select the menu item called “New Control.” That generates a sub-menu that contains all the types of objects you can place in a stack window. Quite an impressive array, right? But don’t panic. I’ll take you through them in incremental steps through the rest of this book.

Find the one called “Label Field” and select it. A label field appears more or less in the middle of your stack window (see Figure 2-8).

Figure 2-8. Creating a Label Field in a Mainstack Window

Page 55: Software at Speed of Sound -- Dan Shafer

30

AN ASIDE: This is a good time to pause for a moment and ask yourself what the differences are between these two approaches to placing objects. Essentially, using the tool palette allows you to draw and approximate the location and size of the control at the time you create it while using the menu-based approach puts an arbitrarily sized object of the type you select more or less in the center of the window. I know lots of Revolution developers who swear by each method. I like the more direct manipulation method of the tool palette but it has one distinct disadvantage: not all controls are listed on the tool palette, necessitating changing an object’s properties (which we’ll learn how to do in Chapter 3) to do what you want to it. (To be fair, it would be impractical to make all of the controls available from the Tool Palette; there are just too many of them. On the other hand, the objects in the “New Object” submenu are not in alphabetical order, so locating the control you want isn’t easy until you become quite comfortable with the order in which they appear.)

You can drag the newly created label field to a point near the upper left corner of the window simply by clicking and dragging anywhere inside the label field.

Now you get to pick. Using whichever method you like (there’s no pressure here; nobody’s going to know!), create a new text entry field object in the stack window so it occupies most of the area of the window as shown in Figure 2-9.

Figure 2-9. Creating a Field in a Mainstack Window and Sizing It

Congratulations! You’ve created your first Revolution stack and card.

What’s that? You don’t remember creating any cards? Oh, right.. I forgot to mention that every Revolution stack consists of at least one card as well. So when you create a new mainstack or substack, Revolution creates a card. Stacks can only contain cards; everything you create and draw resides on a card.

Andrew
Highlight
Andrew
Highlight
Page 56: Software at Speed of Sound -- Dan Shafer

31

To confirm this little nugget of Revolutionary Truth, let’s meet one of the more helpful of Revolution’s built-in tools, the Application Browser. Go to the Tools Menu and choose it. You’ll notice that your stack, which is still called “Untitled 1,” has a little disclosure triangle to its left. Click on that triangle and you’ll see something like Figure 2-10 in the left pane of the Application Browser.

Figure 2-10. Application Browser Shows Presence of Card in Stack

As you can plainly see, there is a card in there. (You’ll also notice that there at least appear to be audio clips and video clips. Revolution creates placeholders for these two types of objects in each stack as it’s created. You don’t need to understand why that’s the case just yet; just know that they’re there even though you haven’t created them.) Now, click on the card and take a look at the right-hand pane of the Application Browser. As you can see, all three of the objects that we placed on the card -- the button, the label field and the field -- are listed as objects contained in the card. The left pane uses an outline metaphor for showing us the structure and objects in our stacks.

Figure 2-11. Card Selected in Application Browser to Reveal its Objects

Andrew
Highlight
Andrew
Highlight
Page 57: Software at Speed of Sound -- Dan Shafer

32

NOTE: If we were building a real application here rather than simply demonstrating basic object manipulation, we would begin by designing and including the menubar for our application. I’ll talk about this in more detail in Chapter 3, but the basic issue involves stack appearance. If you don’t create and place a menubar before you lay out other controls, then when you do add the menubar, it will relocate objects on the screen and frustrate your best design efforts.

Groups and Background Behaviors We are one step away from basic functionality in our note-taking stack. As it stands, we have just one card in our stack. Cards are where Revolution stores data. (Well, it’s actually more complex than that, but for now, assume this is true. It’ll make my job easier and confuse you less.) Assuming we’re going to want more than one note in our note-taking application, we need to be able to create new cards. That’s easy. What’s a little tricky is creating new cards that look like the original we just made.

Right now, if you go to the Object menu and select “New Card,” you’re going to get a blank card. Not very helpful is it? (If you got ahead of me there and actually created a new card, you can delete it from the same menu where you created it. Please do that now.)

What we need to do is to tell Revolution that every time we create a new card in this stack, we want it to have the same three objects that our first card has. This is a two-step process. First, we create a group out of the controls we want to appear on every card. Then we tell that group we want it to reproduce itself on each new card.

Grouping Objects In the stack window, make sure the arrow tool is selected. (If it is, your cursor will be an arrow If it’s not, select the arrow tool from the tool palette.)

Click on one of the objects in the stack window. Hold down the Shift key and click on each of the others. Make sure all three are selected. You can tell because they have selection handles around them as shown in Figure 2-12.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 58: Software at Speed of Sound -- Dan Shafer

33

Figure 2-12. All Objects Selected in Stack Window

OK, now go to the Object menu and select Group Selected. (Alternatively, you can type Command-G or click on the Group icon in the Revolution toolbar.) When you do this, the individual object selection handles disappear and a new rectangle surrounds all of the objects.

To force this group to reproduce itself on every card we create in this stack, we need to define that as a characteristic. We do this in the object inspector. There are lots of ways to open an Object Inspector, again depending on your personal taste. You’ll develop your own programming rhythm over time. The four main ways to open this inspector are:

• double-click on the object

• select “Object Inspector” from the Object menu with the object selected

• Control-click on the object’s icon in the Application Browser

• Command-click or right-click on the object in the window and hold the mouse down to display a popup menu with an option for opening an Object Inspector

Whichever way you go about it, Figure 2-13 shows the Object Inspector that appears for the group object we just created.

Andrew
Highlight
Page 59: Software at Speed of Sound -- Dan Shafer

34

Figure 2-13. Object Inspector for Group

Near the bottom of this inspector palette there’s an option labeled “Behave Like a Background” (see Figure 2-14). This checkbox even has a brief explanation under it in case you forget what it’s for. (If you’re an old HyperCard hand, of course, this is duck soup. Been there, done that, what’s the big deal?)

Figure 2-14. Checkbox to Make a Group Behave as a Background

OK, almost there. In the tool palette, select the Browse tool (the upper-left icon that looks like a pointing finger). Click back in your mainstack window. Type a note into the field. (See Figure 2-15.)

Andrew
Highlight
Andrew
Highlight
Page 60: Software at Speed of Sound -- Dan Shafer

35

Figure 2-15. Text Typed Into Field of Mainstack Window

OK, now here’s the cool part. You ready? Type Command-N or select “New Card” from the Object menu. Voila! A brand-new, clean card ready for some new note-taking appears. Type some text into it. (I’m not going to help you this time. Be creative! Or at least spontaneous.)

You’re probably tempted now to add a bunch of new cards with notes on them, just for the pure joy of it. That’s OK, but before you go a lot farther, let’s see whether our first note is still there. After all, we didn’t save our file, so it might be gone, right?

You need a way to navigate around in the Revolution stack you created. Fortunately, Revolution’s IDE builds this right in. Go to the View menu and select “Go First.” Alternatively, type Command-1. Voila! The first card of deathless prose you typed appears as if by magic.

OK, now you can go create as many new cards with notes as you like. When you’re ready, come back here and we’ll continue this little exercise. We’re almost done.

Saving a Stack Saving a stack is easy, but it has some ramifications that aren’t all that obvious. I’ll cover the basics first and then we’ll explore a few of the related ideas that crop up and sometimes confuse new recruits to the Revolution.

First, remember that a Revolution stack file is different from a Revolution stack. You save stacks in stack files. Stack files have one or more stacks, but only one mainstack. Even though your mainstack is now called “Untitled 1,” you can still save the stack without

Andrew
Highlight
Andrew
Highlight
Page 61: Software at Speed of Sound -- Dan Shafer

36

naming it, but you must give the stack file a name. So, go to the File Menu and select the “Save...” or “Save As...” option. It doesn’t matter which at this point.

Your operating system’s standard file-saving dialog appears. Navigate to the folder where you want the stack file stored, give it a name, and click the OK or Save button or press Return.

Your stack is now saved on the disk. Next time you need it, you can just open it, either from within Revolution or by double-clicking it in your Operating System.

So What’s Tricky? Well, that was trivial. So what are these “ramifications” I mentioned earlier?

There are two, really.

First, you probably don’t want to have your stack named “Untitled 1” forever. In Chapter 3, I’ll teach you how to name your stacks (and for that matter your buttons, fields, and other objects, too). Just know that for now you have a stack file with a hopefully memorable name and that it contains one mainstack and no substacks and that the mainstack is named “Untitled 1.”

Second, what you’ve saved can be run by anyone who owns Revolution, but not by anyone else. So you can open this stack, modify it, play with it, use it in your daily life, make it part of your family if you like. But give it to your buddy who doesn’t have Revolution and nothing’s going to happen. When you want to build a program (called a “stand-alone executable” for reasons you don’t even want to know), you’ll take a stack file you’ve already saved and use Revolution’s revolutionary built-in distribution builder. That’s another sense in which you can be said to “save” a Revolution application.

Creating Scripts Scripting (or programming if you prefer) is the focus of 95% of this book, so we’re obviously not going into a lot of depth here. But you need to understand just the basics of how you create, edit, apply and test scripts.

Let’s give the button in our embryonic note-taking application the ability to create a new card on demand. This involves:

• opening the button’s script editor

• deciding what message we want to respond to

• writing an event handler for that message that does what we want to do

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 62: Software at Speed of Sound -- Dan Shafer

37

• confirming the syntactical correctness of our script (aka making sure it follows the rules)

• testing the button script to make sure it works

Opening the Script Editor Recall that we put the button in this application into a group. If you click on the button to select it, you won’t be able to do that. Instead, Revolution selects the group. After selecting the group, go to the toolbar and click on the “Edit Group” icon. Now when you click on the button, only the button is selected.

As is so often the case in Revolution, there are myriad ways to get at the script editor for an object. Here are the most common:

• select the object and choose “Object Script” from the Object menu

• make sure the arrow tool is selected, position the cursor over the object, and hold down the Option and Command (or on Windows Alt-Control) keys

• select the object’s icon in the Application Browser and Control-click (or right-click), then select “Edit Script” from the popup contextual menu

Yep, this is another one of those “pick your own poison” situations. Try them out. One will probably be more comfortable for you than the others. Once you’ve done this, you’ll see the script editing window for the button open as shown in Figure 2-16.

Figure 2-16. Button Script Editor Window

OK, now click in the editor window between the two lines Revolution supplied for you and type the second line exactly as it appears here. (Yeah, I know there’s a typo. It’s part of the point. Sheesh.) on mouseUp create cad end mouseUp

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 63: Software at Speed of Sound -- Dan Shafer

38

Click on the “Apply” button in the lower right corner of the script editing window. You should see a Revolution IDE error dialog like the one shown in Figure 2-17. Don’t worry about trying to figure out what it’s trying to tell you. You can tell at a quick glance that the problem is that you misspelled “card.” (OK, technically I misspelled it. You were only following orders like a good Revolutionary.)

Figure 2-17. Error Dialog Reporting Problem With Button Script

Close the error dialog. Click back into the script editor and fix the word “card.” Now click “Apply.” Silence is golden.

Save your stack again.

That wraps up my intent in this chapter. In Chapter 3, I’ll help you extend this useful but sort of ugly and brain-dead little stack into something more closely resembling a polished application.

Page 64: Software at Speed of Sound -- Dan Shafer

39

Chapter 3: Using and Managing Basic Building Blocks In this chapter, you’ll learn:

• how to give Revolution objects more meaningful names

• how to arrange objects in a Revolution stack window

• how to change characteristics (properties) of Revolution objects

• the differences between running a stack in the IDE and as a standalone

• minimal menu creation

This chapter builds on the example we created in Chapter 2. If you haven’t worked through Chapter 2 or if you’ve lost your work, you’ll find the work in progress in the SmartEBook™ version of this book or in the Chapter 2 folder of the book’s source code file, available free at my Revolution Web site (http://www.revolutionpros.com, click on “My Stuff” and scroll down).

Picking Up Where We Left Off What we’ve built so far is what I’d call an alpha release. It proves the concept that the basic idea of the program will work and it provides some basic functionality. But it’s not yet feature-complete and, as we’ll see shortly, it would not work well outside the Revolution environment.

For openers, we’ve left things named in such a generic way that they’re not only unhelpful, they’re sort of (dare I say it?) stupid. And we don’t have things very nicely lined up or arranged in the application window, either. Those are trivial things to fix in Revolution, so let’s tackle them first. In the process, you’ll learn a bit about how the IDE works, mastering some basic principles that will be usable the rest of your long career as a Revolutionary.

Creating Meaningful Names for Objects Let’s start with changing the label in our burgeoning application from “Label Text” to something meaningful like...oh, I don’t know, how about “Notes”? As you perhaps began to understand in Chapter 2, object inspectors are the heart of the Revolution IDE, at least when it comes to managing objects. I have adopted a programming style in Revolution that includes leaving an object inspector open all the time in my environment. The contents of the single inspector change as you select different objects, making it trivial to work with the inspector as needed.

But first we have a small obstacle to overcome. Remember that in Chapter 2 we collected all three of our objects into a single group. If you now double-click inside that group anywhere, you’ll get the object inspector for the group object. Not what we had in mind.

Before you can edit individual controls or objects that are part of a group, you have to tell Revolution that you want to edit those individual components. One way to do so is to click on the “Select Grouped” button in the Revolution toolbar. Assuming you’ve left

Andrew
Highlight
Andrew
Highlight
Page 65: Software at Speed of Sound -- Dan Shafer

40

your toolbar as it was by default when you began using Revolution, the icon is clearly labeled; Figure 3-1 shows where it is on the toolbar.

Figure 3-1. Edit Group Icon on Revolution Toolbar

Clicking this icon puts you into group-editing mode. Until you tell Revolution that you’re finished editing the group, you can work with individual objects. Furthermore, any new objects you add to the window while you’re in group editing mode are automatically added to the group when you stop editing the group.

Now that you’re in group editing mode, select the Label Field. Now open the object inspector for this object if one isn’t already open. Remember, you can do that by double-clicking it, or by selecting “Object Inspector” from the Objects menu. Figure 3-2 shows the object inspector for the Label Field.

Figure 3-2. Label Field Object Inspector

Page 66: Software at Speed of Sound -- Dan Shafer

41

There is, as you can see, a fairly staggering number of things about this Label Field object that you can control with various settings. Take a minute to look over the contents of this page of the object inspector. You’ll notice that its name is “Field 1”.

Let’s change the name of this label to Notes and see what effect that has. Select the name “Field 1” and type over it “Notes” and then tab or click out of that field. Notice anything different in the application window?

Of course you don’t! That’s because the name of an object is simply the name by which you refer to it in your scripts. The name of an object and what it displays are, as a rule, disconnected from one another. (Revolution actually breaks this rule when it comes to buttons, as we’ll see soon, but for the most part this rule is true.)

So how do we change the text being displayed by this field?

The answer lies in the contents of the innocent-looking popup menu at the top of the object inspector palette window. Click and hold that and you’ll see that this palette has a number of different pages (or panes), each devoted to a different collection of object settings. It probably doesn’t require great programming knowledge to figure out that what we probably want to edit here is the “Contents” of the object. Select that menu option and the object inspector changes to look like Figure 3-3.

Figure 3-3. Editing Contents of a Label Field in the Object Inspector

Select all of the contents of field containing the text “Label text” and type “Notes” again. Tab or click out of the field. Now you can see that you have indeed changed the contents of the label field in the application window.

Now, select the button in the application window. Notice that the object inspector is now showing the properties of the button. You’ll also notice that the object inspector palette returns to showing you Basic Properties of the button object. In most cases, when you change selections in your application window, the object inspector switches to that

Andrew
Highlight
Page 67: Software at Speed of Sound -- Dan Shafer

42

object but keeps the old page in view. In other words, if you’re editing the contents of one field and you click on another field, the object inspector switches its view to the new field but keeps the “Contents” page in view.

The button object has no Contents view, so the object inspector politely takes us back to examining and editing the Basic Properties of the object. The screen now looks like Figure 3-4.

Figure 3-4. Button Object Inspector

As you can see, a Button object has two properties listed near the top of the inspector palette: Name and Label. Just for grins, let’s change the name, even though we just

Page 68: Software at Speed of Sound -- Dan Shafer

43

learned in editing the Label Field object that this should have no visible effect. Select the text “Button 1” and change it to “New Note,” then click or tab out of the field.

The name of the button dutifully changes. What’s going on here?

A button will display its name attribute as if it were its label unless you also supply a label attribute, in which case it will use the label attribute in displaying the button.

In any case, you could just leave the button as it is now, but that has some minor disadvantages when it comes to scripting, so I recommend that you do two things:

1. If you’re going to give names to objects for easy reference in scripts later, make the names one word long, even if that means joining several words together in a single word-looking string.

2. Change the label on the button to what you want it to be, including spaces for words.

Not every Revolution programmer would agree with that advice, but they’re not writing this book, so you get to hear my opinion here!

I chose the name “newNoteButton” for the button and labeled the button “New Note.” You can follow suit or do whatever you like. It’s your Revolution. Figure 3-5 shows you what my button object inspector looks like when I’ve named and labeled the button.

Page 69: Software at Speed of Sound -- Dan Shafer

44

Figure 3-5. Button Inspector After Changes

NOTE: If you were very observant, you will have noticed that when you changed the name of this button to “newNoteButton” and then tabbed or clicked into the Label field, the button’s label changed to reflect the new name you gave it. This is the only place in Revolution I’m aware of where the IDE renames an object whose label you have not yet changed. It’s helpful because sometimes you can just use the name as the label. But in this case, the name we gave the button for scripting purposes isn’t user-friendly as a label.

This is a good time to save your work. In fact, it’s tough to come up with a really bad time to save your work other than perhaps after you’ve just made what I’ve come to call a “catastrophic improvement” and now things don’t work any more. In that case, see the “Revert so Saved...” option on Revolution’s File menu.

Arranging Object Layouts The next step in cleaning up our application’s appearance is to position the objects in the application window so they are aesthetically pleasing. The bad news is, aesthetics are subjective. For example, I like to arrange photographs and pictures on my office wall in a pattern I call “asymmetrical” and my wife calls “jumbled.” Go figure. So let me say at the outset here that I’m not providing any guidelines for what the arrangement of your

Andrew
Highlight
Page 70: Software at Speed of Sound -- Dan Shafer

45

objects ought to be; I’m just showing you how to move them around so you can put them where you like them, OK?

There are three ways to arrange objects in your Revolution window:

• direct manipulation (dragging them around)

• editing their location information in their object inspectors

• aligning them to one another using Revolution alignment techniques

These ways are not mutually exclusive. In fact, in the course of a Revolution project of any complexity, I use all three of them, sometimes on one object.

Select the Label Field. Holding down the mouse, drag it around a bit. Place it slightly above and as closely as possible aligned with the left edge of the note field. Release it and click somewhere else in the window. Once you’re happy with its position, select it again. From the object inspector’s popup menu, select “Size & Position.” The inspector looks like Figure 3-6.

Figure 3-6. Label Field Inspector Showing Size & Position Settings

Andrew
Highlight
Page 71: Software at Speed of Sound -- Dan Shafer

46

Remember the number that’s in the Width field so you can go back to it. (It’s probably 100 since that’s the default width but it could be different.) Try adjusting the width by typing in a new width and tabbing or clicking out of the field, or with the tiny scrollbar control to the right of the window. Try resizing the field manually by dragging one of its handles and watch what happens to the Width setting as you release the mouse.

Note that it is possible to supply a value for the Width or Height settings of the object that are so large that part of the object disappears from the screen. This can also happen if you have a wide object and resize the window to be too small to accommodate its width. It’s a good idea to remember that in such situations you can always return to the object inspector and force the object back into view where you can rearrange it to suit your new window size.

OK, return to the original value for now.

Next, select and move the button around. See how close you can get it to the horizontal center of the window. Now let’s see how close you came while learning a new technique for centering objects in a horizontal or vertical space.

Open the Stack Inspector. You can do this from the Object menu, by clicking anywhere in the window where there are no objects, or by selecting “Inspect” and “Stack” from the popup menu that appears when you click the small right-pointing arrow at the upper right of the object inspector palette. Select the “Size & Position” option from the inspector’s menu if it isn’t already showing. The resulting palette looks something like Figure 3-7.

Figure 3-7. Stack Inspector Showing Size & Position Value

Make a note of the width of the window.

Page 72: Software at Speed of Sound -- Dan Shafer

47

Now select the button again and notice its horizontal position. This number should be exactly half of the width of the stack window. If it isn’t, type in a new value or use the scrollbar to adjust it.

Fiddle around with the positioning of the three objects in this window until you have them more or less where you’d like them. Then save your work.

As we bring this topic to a close, let’s look at how we can use Revolution to align objects to one another in a way that creates a symmetrically pleasing layout. On the Object menu, Revolution provides a submenu option called “Align” (see Figure 3-8).

Figure 3-8. Align Submenu of Objects Menu

There is only one “trick” to using object alignment in Revolution. The first object you select becomes the base, or home, object to which all the other objects you subsequently select will be aligned. In this case, we’re going to see if we can align the notes field to the label field. Select the Notes field, then shift-click and select the Label Field. Now from the Object | Align Selected Objects menu, pick “Left.” The result in the application window should look like Figure 3-9.

Andrew
Highlight
Page 73: Software at Speed of Sound -- Dan Shafer

48

Figure 3-9. Label Field Aligned With Notes Field

If you look closely, you’ll see that the word “Notes” is a few pixels to the right of the border of the Notes field. Why is that? Didn’t you just tell Revolution to line them up perfectly? Your bewilderment will only increase if you look at the Size & Position settings of the two fields independently of one another; they both show identical left positions. So what’s going on?

This one baffled me for a bit until I realized what Revolution is doing. It is aligning the edges of the fields, but the fields have borders, but the border widths don’t match thanks to different default settings. That’s what makes the alignment appearance different from what you might have expected.

There are two easy ways to fix this problem. You can increase the width of the border of the label field to match that of the text field. Or you an increase the label’s margin settings by enough points to factor in the field’s border width.

Let’s dig just a little deeper into this positioning business for objects in a Revolution stack window. We usually want to put new notes into our note-taking application after the most recent note we’ve added. It would be perhaps useful if we always make sure we’re positioned at the last note in the stack before we add a new note. (There are other, less manual ways of accomplishing this task, but this gives me a chance to demonstrate a couple of other layout techniques you’ll find useful from time to time.)

Drag the “New Note” button to the left a bit and then create a new button. Make this new button deliberately bigger or smaller than the New Note button. Name it “lastNoteButton” and label it “Last Note.” Open its script editing window and type in the following handler:

Page 74: Software at Speed of Sound -- Dan Shafer

49

on mouseUp go last card end mouseUp

Click the “Apply” button and close the script editor. You should now have something that vaguely resembles Figure 3-10.

Figure 3-10. Newly Placed and Named Button

We want to have both buttons be the same size and shape. (That’s just good user interface design.) Select the original “New Note” button, then shift-click to select the new Last Note button. Go to the Object Menu and, from the Align Selected Controls submenu, pick the following items in succession:

• Make Heights Equal

• Make Widths Equal

• Top

The result will look something like Figure 3-11. Notice, too, that Revolution has kept the two buttons selected, so you can now move them as a unit to put them where you want, knowing their top edges will stay aligned.

Page 75: Software at Speed of Sound -- Dan Shafer

50

Figure 3-11. Two Buttons Aligned and Selected

Setting Properties for Revolution Objects Everything about any control or component in a Revolution application -- from its location and appearance to its name to how it behaves when the window that contains it is resized, to how it responds to tab keys, and a host of other characteristics -- is defined as a property of the object. You can control every property of every object in the Revolution environment by modifying its properties.

You can change properties in two ways: through Transcript commands, or through the object inspector.

We have already done a good bit of property-setting for Revolution objects. In this section, I’ll discuss some of the major categories of properties that are most commonly used in Revolution. I’m going to focus on the object inspector; the rest of the book is devoted to scripting approaches to application development, but this is one of the few times we’ll take a non-programming approach to a situation.

There are more than 100 properties that virtually all Revolution objects have in common. Think about that for a moment. Over 100 individual properties for each and every type of object in Revolution. That’s thousands of properties before we start digging into individual object types and their unique properties. But now is not the time to panic. Even though this may seem daunting, most of these you’ll never use. When you need one of them, you’ll love the simplicity of controlling them. There’s a single model for working with properties that is refreshingly clear.

Page 76: Software at Speed of Sound -- Dan Shafer

51

Some individual objects have another 100 or more unique properties as well. And as if that weren’t enough, you’ll find that defining and using custom properties -- properties that you define and manage through your scripts -- is another powerful Transcript programming technique. Obviously, you can’t possibly be familiar with all the properties in Transcript. In Chapter 4, we’ll look at every Revolution object type and get some idea what their chief properties and characteristics are.

All of this gives rise to one Revolutionary Rule. (I know; revolutions don’t have rules; that’s why they’re called revolutions. Just think of me as kind of a traditional revolutionary who’s comfortable with a few rules here and there.) If there’s something -- anything -- about an object you don’t like or want to change for some reason, assume there’s a property for it and set about finding it. (Incidentally, this rule doesn’t apply just to individual controls and objects. The Revolution environment -- both the IDE and the runtime world where your standalone applications execute -- has global and local properties you can control only through Transcript commands and scripts. There are more than 200 global properties and another dozen local ones. A global property applies to the entire application while local properties apply only to a specific object or type of object.)

Common Types of Properties All Revolution objects have the following seven categories of properties:

• Colors & Patterns

• Custom Properties

• Database

• Geometry

• Inks

• Property Profiles

• Size & Position

• Text Formatting

Colors & Patterns properties include those that determine the background, foreground, and border colors or patterns associated with an object’s appearance. Figure 3-12 shows the Colors & Patterns property sheet from the object inspector for a field. Note that all of the options are labeled. Now look at Figure 3-13, which shows the same page of an object inspector palette for a player object. As you can see, most of the options are turned off for a player object because they’re not relevant. Using this technique, Revolution is able to keep what has the potential for being a very cluttered and confusing user interface very clean and predictable.

Page 77: Software at Speed of Sound -- Dan Shafer

52

Figure 3-12. Colors & Patterns Property Settings for a Field

Figure 3-13. Colors & Patterns Property Settings for a Player

Custom properties are defined for each instance of an object in the application. In other words, a single button might have a custom property but that doesn’t mean that all buttons in the application share that custom property.

Database properties are only relevant to container objects (principally fields) and buttons (where database queries are often stored).

Page 78: Software at Speed of Sound -- Dan Shafer

53

Geometry properties define how an object behaves when the window of which it is a component is resized. Figure 3-14 shows a typical geometry property sheet.

Figure 3-14. Geometry Properties Sheet

Property profiles are a mechanism for collecting together a set of properties into a single profile that allows you to change significant portions of an application with a single mouse-click. This is really advanced stuff. It allows you to define a number of different special types of field, for example, each with an associated property profile. To use a field like that, you create a field and then apply the property profile to it. This is a way to group a bunch of related property settings for a particular type of object together for ease of use.

We’ve already spent some time with the Size & Position property settings process.

The text formatting property sheet (see Figure 3-15) allow you to determine the font, size, styling, alignment, margins, and case of the text used in specific containers.

Page 79: Software at Speed of Sound -- Dan Shafer

54

Figure 3-15. Text Formatting Property Sheet

Running Alone is One Thing As long as you run your stacks in Revolution, you have access to the entire IDE to support you. But when you want to take your super-duper note-taking application to the great unwashed and convince them to part with real money for the privilege of using it, you can’t assume that all your customers will also own copies of Revolution (much as we’d all like to see the installed base of users grow). You’ll want to build one or more distributions of your product. A distribution is a stand-alone application that users can install and run without owning Revolution.

But what part of the IDE won’t be available to users when they buy and install your world-beater of a program? Revolution makes it incredibly simple to find out.

From the Development menu, choose “Suspend Development Tools.” The standard IDE menu bar disappears along with all the windows you’ve had open to work on your stack. You’re left with your mainstack, a minimalist menu which essentially does nothing, and a floating palette (see Figure 3-16) with a single button with which you can restore the development environment.

Figure 3-16. Palette for Restoring Development Tools

You’ll notice that there’s no way to quit the application, and certainly there are no navigational tools to help the user get from one card to another. Your buttons still work,

Page 80: Software at Speed of Sound -- Dan Shafer

55

though (confirm this) and the application has all the functionality it has in the IDE and not a speck more.

To make this application minimally functional outside the Revolution IDE, then, you’re going to have to supply some way to let the user navigate through the stack of notes and quit the application. Ultimately, of course, you want the ability to timestamp, print, and delete notes as well, perhaps, as the ability to find notes by their content, and go to the first note in the stack.

I’m not going to take you through all the decisions and design and scripting work it would take to finish the application. I’ll leave that, as they say in infuriating textbooks, as an exercise for the reader. But I’d be remiss if I didn’t at least help you dip your toe into the wacky world of Revolution menus.

Adding a Menubar to Your Application As a matter of good Revolution design, I always recommend that you tackle the menubar before you go too far into the process. In part, this is because the menubar is a crucial element of most applications. In part, it’s because if you wait too long into the development cycle, fitting the menubar into the design can become a really nasty stumbling block, particularly on the Mac, which differs in its menu implementation from Windows and Unix systems enough that real pain can arise if you don’t create your application’s menubar(s) at the outset, before you place any UI components in the window.

NOTE: If you are converting an existing HyperCard stack or if you find yourself already deep into creating a stack when you realize you need to accommodate the different way the Mac approaches menubars, then you will greatly benefit from reading Jacque Landman Gay’s excellent tutorial on converting HyperCard stacks to Revolution. The tutorial is at http://www.hyperactivesw.com/mctutorial/rrtutorial.html. Specifically, check out the wonderful handler Jacque has written that moves all objects in your application to an appropriate point to accommodate the menubar placement after the fact. That script is discussed and provided at http://www.hyperactivesw.com/mctutorial/rrcreateMenus.html.

Menu creation and management can be a fairly hairy task. I devote much of Chapter 12 to it. In this brief section, I just give you a taste of the technique and show you how to build a basic menubar. Along the way, I’ll teach you how to find and steal code for your applications. No sense re-inventing the wheel, right?

Menubars: An Overview Menus and menubars in Revolution consist of buttons that are given special characteristics and then gathered together into a menu or menubar.

At the outset, let’s be clear about the terminology we’re using.

Page 81: Software at Speed of Sound -- Dan Shafer

56

A menubar is the collection of menus that appears across the top of your screen (on a Macintosh) or application window (on Windows or Unix).

Every menubar has one or more menus. A menu is the label that tells the user what the options associated with that menu are designed to do.

Each menu has menu items, which are the elements that cause something actually to happen when the user selects them. Some menu items are actually submenus which in turn have menu items.

Not all menus appear in menubars. There are context and popup menus all over the place in Revolution and they’re pretty common in Revolution applications as well. But we’ll focus here on menus that appear as parts of menubars.

The Revolution Menu Builder Revolution’s IDE includes a number of very helpful tools that guide you through the process of some of the more complex undertakings in application development. Let’s focus on one of them now. Go to the Tools menu and choose “Menu Builder.” A window like the one in Figure 3-17 appears.

Figure 3-17. Menu Builder’s Main Window

Andrew
Highlight
Page 82: Software at Speed of Sound -- Dan Shafer

57

Click on the “New...” button in this window. A window like the one in Figure 3-18 appears. This window enables you to create a basic menubar with tremendous ease. As a routine, I change the name of the menubar to...well...Menubar. There is only going to be one menubar in most of my applications, so I’ve never seen the need to get fancy.

Figure 3-18. Menubar Creation Window

The scrolling field in the middle of this window lists the menus your new menubar will have by default. For the purposes of this limited demonstration, we’ll delete the Help and Edit menus and add a new menu we’ll cal “Go” that provides menu-based navigation among cards (notes) for the user. After making those changes, click on “OK in this window and a window like Figure 3-19 appears.

In the left-center portion of the screen, all the menus are listed. As you click on each of these in turn, the right-center portion of the screen shows you the menu items associated with that menu. Figure 3-19 shows you that the basic File menu provided by Revolution as a default contains menu items for New documents, opening existing documents, closing an open document, and quitting the application. That’s handy, since those are standard menu items. Our newly added “Go” menu is, unsurprisingly, blank, since we haven’t yet told Revolution what we want to do with that one.

Page 83: Software at Speed of Sound -- Dan Shafer

58

Figure 3-19. Default File Menu and Its Item, Quit

Select the “Go” menu and then in the right part of the window where you’re dealing with menu items, click on “New Item.” Give the new item the name “Last Note.” Then, at the very bottom of the right side of the window, check the checkbox next to the Shortcut label and put the number 4 into the little editing box to the right. At this point, your window should look a lot like Figure 3-20.

Page 84: Software at Speed of Sound -- Dan Shafer

59

Figure 3-20. First Menu Item of New “Go” Menu Created

We are using the standard Revolutionary navigational shortcuts here:

• Command-1 is the first card

• Command-2 is the previous card

• Command-3 is the next card

• Command-4 is the last card

Go ahead and add the other three Go commands to the menu. When you’re done, you can use the arrows just above the list of menu items to rearrange them in the order you’d like. When you’ve finished, your window should resemble Figure 3-21.

Page 85: Software at Speed of Sound -- Dan Shafer

60

Figure 3-21. All “Go” Menu Items Added

Click on the checkbox labeled “Set as Menubar on Mac OS” and you have a menubar in place in your application. Just close the Menu Builder window for now. It may or may not be obvious, but before these menus and their items are actually useful for something, we have to attach scripts to them. It’s a bit early in your Revolution experience to learn to do that. In fact, I won’t get back to menus until Chapter 12. However, if you’re just dying to see how to hook up these menus, you can either skip to Chapter 12 or you can go look at the stack for that chapter with the menus all scripted. Then you can look at the scripts in the script editor of the Revolution IDE and see how to do this stuff. In fact, one of the best and fastest ways to learn Transcript is by inspecting the scripts of existing projects and products.

In a sense, this book will give you the training to be a Revolutionary; other peoples’ stacks will give you the ammunition.

Page 86: Software at Speed of Sound -- Dan Shafer

61

Chapter 4: Revolution Building Blocks In this chapter, you will learn about the items that make up Revolution, including • action elements • passive character traits • objects This chapter presents an overview of these Revolution and Transcript building blocks; each is discussed in detail in one or more places later in the book.

Naming Things in Transcript Before we begin our examination of the components of the Transcript programming language, we should pause to discuss the rules regarding the names of things in Transcript.

Action elements and variables in the Transcript language are generally made up of one word. Action elements include functions, commands, and messages. If you define a new function designed to reverse the characters in a string, for example, you can call it reverse, reverseString, reverselt, or something similar. But you cannot name it reverse the string because that name has more than one word. Similarly, variables, a type of special item called a container in Transcript, must also have one-word names. Passive elements other than variables are not generally named; they are more correctly viewed as part of the structure of the language, as we will see in a few moments.

Objects can be named almost anything you wish, using as many words as you like. Thus, you can have a stack named My Important Stuff and a card named Books and Magazine Articles. Buttons frequently have two- or three-word labels (though you have to watch the length and make sure it can be seen through the button if you have Runtime Revolution show the label on the button). This makes Runtime Revolution friendlier for users. A button named Do It! is more communicative than one called dolt, which might in fact also be misread as an insult!

Several words in one One naming technique Transcript scripters often use is to run two or more words together into one and then capitalize the first letter of each embedded word. You'll see labels like currentStackName, mouseUp, and passMeTheSalt sprinkled through the Transcript script examples in this book and other scripts.

This approach to naming things is not unique to Transcript. Programmers have been using the technique for many years. But some programming languages have limits on the lengths of names that make the use of such an approach marginally useful at best. In Transcript you won't encounter any limit that will become problematic. A button or field, for example, can have a name up to 253 characters long. (While you could do that, I’d be inclined to suspect you were just showing off.)

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 87: Software at Speed of Sound -- Dan Shafer

62

The first character is important The first letter of a variable name in Transcript must be either an alphabetical character or an underscore. Transcript developers generally use an underscore as the first character of a variable sparingly and usually only to indicate there is something special about the variable.

Revolution Application Structure

A minimal Revolution application consists of a single file that contains one or more stacks. The file is sometimes referred to as a “stack file.” Each stack has one and only one window associated with it and generally has one or more cards as well. Each card in a stack may be different from all the rest or all of the cards in a given stack may have a similar or identical appearance to all the other cards in that stack.

Each stack (window) in a Revolution application can be displayed as a normal, editable window, or as a palette, a modal dialog box, or a modeless dialog box. (A palette is generally a small window with icons, which may or may not be labeled with text as well, from which the user makes one-time selections of tools and commands. A modal dialog is one that the user must respond to before the program will allow continued operation, while a modeless dialog is one the user need not deal with immediately.)

On Macintosh OS X, the special type of dialog called a “sheet” is also supported by Revolution. (A sheet is a dialog box that appears to descend from the title bar of a window and to be contained within the window as well as moving with the window.)

Active Elements of Transcript An active element, for the purpose of our discussion, is any component of Transcript that results in something happening in the environment. Structurally, you can think of two basic types of active elements: commands and functions. Commands tell Revolution to do something. Functions tell Revolution to calculate a value or determine and return some information to the place they were called. Commands and functions, in turn, are the primary elements that comprise handlers and scripts, which in turn are the basic programmatic building blocks in Revolution.

A handler is a Revolution program component that executes commands and functions. Each handler responds to -- or “handles” (thus its name) a “message.” Messages are the transport mechanisms on which commands and functions travel and along which functions return their results.

Tying it all together, Revolution behaves in response to messages which are triggered by the user or by the system. Each message looks for a handler of the same name and, if it finds it, begins running the commands and executing the functions contained in that

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Sticky Note
Marked set by Andrew
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 88: Software at Speed of Sound -- Dan Shafer

63

handler. A group of handlers related to a specific stack, card, or other object, is called a script.

Messages We spent some time in Chapter 2 describing messages and their role in Transcript. Now let's examine a message structurally.

All Transcript message names, like all Transcript names, consist of only one word. That important idea is sometimes hard to remember. Because Transcript includes the ability to add throw-away words, some messages look like they're longer than one word. But only the first word of a message is the message itself. Everything that comes after is one of three things: a parameter; additional descriptive information (to tell Transcript what object to affect with the message, for example); or “throw-away” words that are allowed in specific commands to make Transcript read more like English.

The rule that only the first word of a message is its name becomes important when you write handlers to respond to messages. The handler must be associated with a message name. Requiring that all message names be one word makes life much easier for us scripters!

Commands A command in Transcript is a language element that tells Revolution to do something. It requires that your stack or application take some action: sound the system speaker (the beep command), switch its view to another card (the go command) or place a value into some container (the put command), for example. Transcript includes a large number of built-in commands.

Functions Functions are of two types: built-in Transcript functions and user-defined functions. Transcript has well over 100 functions that can be used in any handler. These functions involve such tasks as:

• mathematical calculations • locating and managing the mouse and its button • dealing with date and time

In addition, you can define any function you need in function handlers.

Handlers There are two types of handlers in Transcript: event handlers (also known as message handlers) and function handlers. An event handler always begins with the key word on followed by the name of the message it is designed to handle. It also ends with the key word end followed again by the name of the message it handles. Most handlers you write will be event handlers.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Rectangle
Andrew
Rectangle
Andrew
Rectangle
Andrew
Rectangle
Andrew
Rectangle
Andrew
Highlight
Page 89: Software at Speed of Sound -- Dan Shafer

64

A function handler begins with the key word function followed by the name of the function it defines. The purpose of this handler is to allow you to define new operations that can be used by other event handlers. This type of handler also ends with the key word end followed by the name of the function involved.

After you define a function in a function handler, it is available to all other event and function handlers in the same script or lower in the Transcript hierarchy. (The hierarchy is an important concept that we’ll visit in detail in Chapter 5.) A function handler cannot be defined within another function handler or an event handler.

There are some operational differences between these two types of handlers. This subject occupies much of our attention in Chapter 5. For now, it is useful to know that a function is used where you want the handler you call to return to you some value to operate on, while a message handler processes the message and does not return a result to the handler that called it.

Scripts A script in Transcript is a collection of one or more handlers — some or all of which may be empty — associated with a particular object.

You can think of a script as a program, but as you know from Chapter 2, that is an over-simplification. There are no “programs” in the traditional meaning of that word in a Revolution stack. Instead, you have one or more scripts, each composed of one or more handlers, which, taken together, constitute the methods describing the stack's behavior in response to messages.

Passive Traits Not everything in Transcript is active. Some elements of the language are for the convenience and use of active elements. These include:

• variables • properties • control structures ;j • "chunks"

Variables as containers of information Throughout this book and other Runtime Revolution and Transcript documentation, you will find the term container used quite frequently. It is easy, after a brief acquaintance with the term and its use, to conclude that a container is nothing more than a variable in a conventional programming language. That view, however, is too simplistic to be useful or accurate.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 90: Software at Speed of Sound -- Dan Shafer

65

A variable is a type of container. But in the broader sense a container is anything that can be a repository of a value. Fields, the message box, and two special Transcript variables called It and selection can also hold information and so are containers.

All programming languages embody the concept of a variable. A variable is any word or symbol whose associated value can change as the program executes or from one execution of the program to another. This variability of value is where variables get their name.

In many programming languages, you have to define or declare variables explicitly before you can use them. This is not the case in Transcript. The rule is simple: When Transcript encounters a word in a place in a script that calls for a container of some sort, and that word cannot be interpreted as the name of an object or as a chunk of an object's contents, it assumes the word is a variable.

It really is that simple. In practical terms, this means that to use a variable in Transcript, you simply use it. No need to declare it, define its type, or otherwise alert Transcript to its existence or nature. Just use it and Transcript takes care of the details. There are actually four types of variables in Transcript. Global variables are accessible to any handler in a script and to other scripts in the Runtime Revolution environment. They must be explicitly declared global using the key word global in any script or handler where they are used. (It is actually just a bit more complicated than this. If you define a script global variable by using the keyword global in front of a variable name in a script but outside the scope of any handler in the script, then all the handlers in that script will have access to that global variable’s value.)

Local variables are known only inside the handler in which they appear and require no special handling. Script local variables are known within all handlers comprising a script, but not to any other scripts or their handlers. Special variables are furnished by the system and include It, selection, and message box. We will have more to say about these variables as they are used in subsequent chapters. (By the way, this may seem to contradict what I said a couple of paragraphs back about not needing to declare variables in Transcript explicitly. It doesn’t. We are dealing here not with declaring the variables but rather defining the type and scope of the variable.)

Let’s look at a small and meaningless but illustrative example. Imagine the following two-handler script is associated with a button in your stack:

global gRunningTotal on mouseUp global gValue answer “Interim total=” && gRunningTotal + gValue put “3” into t1

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 91: Software at Speed of Sound -- Dan Shafer

66

end mouseUp on mouseLeave global gValue answer “The new value of gValue is” && gValue answer “gRunningTotal is now” && gRunningTotal put “6” into t1 end mouseLeave

Don’t worry about trying to understand what is going on in the handlers. Their only purpose is to illustrate the use of differently scoped variables.

The global variable called gRunningTotal is an example of a script global. Its value is known to every handler in the script (but not to handlers in other scripts in the stack). A second global called gValue is also defined but notice that it must be declared in both of the handlers where it’s being used. Presumably there would be other handlers in the script; they would all have access to the value stored in the variable gRunningTotal and could modify it but they could not “see” the value of gValue unless it is declared within their definition as a global. Meanwhile, t1 is a local variable in both handlers. Its value in one has no effect on the value of the same-named variable in another handler. They are completely separate.

Properties All 12 Transcript objects have certain properties associated with them. For example, they all have system-assigned ID numbers and optional names. They also have properties such as location, font characteristics, border, shape, and style.

These properties are all accessible to your scripts, and many can be changed from a script (as well as, more conventionally, through dialog boxes or other user-oriented means). Properties are often examined and decisions made based on the outcome of the examination. For example, you might want to check if a particular object is visible and, if not, to make it visible. Its visibility is a property that your script can both examine and change. Properties are a very important idea in Transcript. We’ll spend much of the rest of this chapter looking at basic properties of the most commonly used Revolution objects.

Of particular value is the notion of custom properties, which are the subject of an eBooklet available through my Revolution Pros site at http://www.revolutionpros.com.

Control structures Transcript offers elements called control structures that permit the programmer to alter the normal sequential flow of processing. Repeat loops, if-then-else conditional processing, and switch constructs are built into Transcript, as is the unique and ultra-fast repeat for each statement. These form the subject of Chapter 8.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Rectangle
Page 92: Software at Speed of Sound -- Dan Shafer

67

Chunks The idea of chunking is unique to hypertext; Transcript, true to its roots, incorporates the use of chunks in its programming.

Simply stated, a chunk is any arbitrary portion of any container. A chunk expression uniquely identifies any given character(s), word(s), item(s), or line(s) in a container. You will come to appreciate chunks as you learn to program in Transcript. Using chunking expressions, you can access individual elements of a container as easily as stringing together a kind of map to their locations. The map is built "inside-out," though, beginning with the smallest unit and moving out to the larger. Here are some examples. (Don't worry if some of the terminology isn't clear; it will be soon.)

second character of word 3 of fifth line of field 1 of card "Help" char 5 to 8 of third word of testVariable third item of It -

You can see how powerful an idea chunking is. It permits you to gain precise control over many situations and data elements in a Transcript script. Soon after you begin scripting, chunking becomes second nature. We’ll spend a good bit of time on chunking expressions in Chapters 5 and 10.

Objects We have spent a lot of time looking at objects in Transcript (see Chapter 2). Let’s take a brief look at each object type from a programmer's perspective.

Some Common Characteristics

While all Revolution objects are different from one another, they do share some common traits.

Here is a list of characteristics most or all Revolution objects share.

• They have inspector windows where you can control many of their scriptable properties without typing any Transcript code. You generally open an inspector on an object by double-clicking on it, but stacks and cards require that you select their inspectors from the Object menu. You can also right-click (or, on the Mac, Command-click) on any portion of the window where no object has been placed, to pop up a contextual menu where you can choose to edit the stack or the card.

• They have a name. Some objects have both a name and a label. You use the name to reference the object in a script while the label is displayed to the user.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 93: Software at Speed of Sound -- Dan Shafer

68

If you assign an object a name but not a label, the name becomes the user-visible label.

• They have text formatting rules, which are inherited if they’re not changed. For example, if you define a stack as having text formatted in Arial 12-point bold, all text in all controls and fields created in that stack will appear as Arial 12-point bold unless you specifically define a different format for a particular object.

• They have geometry rules that tell them how to move or resize if the user resizes the stack window. (Stacks and invisible objects such as audio and video clips, discussed below, do not have geometry values.)

• They can have custom properties defined by the user or designer.

Let’s take a quick look at each type of object and the interface elements associated with creating and managing them in the Revolution development environment. This discussion is intended as an overview; specific object types are discussed in subsequent chapters. Notice, particularly, that we don’t delve into the multitude of properties that each object type can have. The illustrations showing Property Inspector windows on each type of object are merely representative; each Property Inspector has multiple property views, each selected from the popup menu near the top of the inspector’s window.

Stacks Every Revolution application has at least one stack, referred to as its “mainstack.” Many applications have only one stack, but there is no theoretical limit on the number of stacks your Revolution application can contain. However, each application can have only one mainstack; the mainstack in turn can contain as many substacks as necessary to accomplish the program’s purpose. You can dynamically change which script is viewed by Revolution as the mainstack while your program is running.

Each stack has a window. In fact, for many purposes you can think of a stack and its window as being synonymous. Every stack also has at least one card. When you create a new stack, Revolution creates one blank card in it. A mainstack can contain all Revolution object types plus substacks. A substack can contain all Revolution object types, but not other substacks.

A Revolution application is stored in a single file called a stack file. The stack file has at least one mainstack and optionally additional substacks. When you save your Revolution application, you give it a name. This name applies to the application as a whole; the mainstack name need not bear any relationship at all to the name of the stack file.

You can open a stack inspector window (see Figure 4-1) by selecting “Stack Inspector” from the Object menu.

Andrew
Highlight
Page 94: Software at Speed of Sound -- Dan Shafer

69

Figure 4-1. Stack Inspector Window

Figure 4-2 shows the same window as in Figure 4-1, with the popup menu at the top of the window open so you can see all the types of properties for which you can define values in the object inspector interface.

Page 95: Software at Speed of Sound -- Dan Shafer

70

Figure 4-2. Stack Inspector Window Showing Popup Menu for User-Settable Characteristics

As you can see, you can define a great deal of your stack’s behavior and appearance without writing a single line of Transcript code. Good Revolution design suggests that you set any property whose value is accessible through an inspector directly in the inspector rather than resorting to scripting. Then you can use scripting to change properties dynamically in response to the needs of your program.

Groups Recall that in Chapter 1, we introduced the concept of groups. Now let's take a deeper look at groups (which are also called backgrounds) from the viewpoint of the stack designer.

A group is a Revolution object that contains one or more other objects or controls, including potentially other groups. Each object contained in a group retains its own identity and can be manipulated and scripted separately from the group. But the group adds another dimension of control and interaction to the Revolution application. You can modify properties of a group and a group can receive messages and handle user-

Page 96: Software at Speed of Sound -- Dan Shafer

71

driven events if you script it to do so. In the Revolution hierarchy, as we saw in Chapter 2, groups fall between individual objects and the card on which those objects appear.

It is not necessary to define any groups for a stack or card. It is also legal to have multiple groups in a given card or stack.

In many stack designs, you will want to create numerous cards which share some elements in common but which also have unique objects and characteristics as well. Groups are particularly well-suited to this design task. By setting a group’s backgroundBehavior property to be true, you set up a condition under which each new card created either at design time or while the stack is executing, will be created with that group in place. This in effect gives you a way to share common objects, layouts, and behaviors across cards without having to re-create the elements on each card explicitly.

NOTE: The backgroundBehavior property derives its name from HyperCard, where there was no concept of groups but where all cards had one and only one background but a stack could define multiple backgrounds. Experienced HyperCard developers should be sure to read the online document, “About Revolution for HyperCard developers” at http://support.runrev.com/resources/hypercard.php for more detailed information about how groups and backgrounds differ. There are enough similarities to cause confusion if you don’t read and internalize that information.

Figure 4-3 shows the Group Inspector window in which basic properties for groups can be defined.

Page 97: Software at Speed of Sound -- Dan Shafer

72

Figure 4-3. Group Inspector Window

Cards Every card can have its own fields, buttons, and graphics that don't automatically appear on any other card in the stack. These buttons and fields are accessible only from the card on which they appear, though it is perfectly permissible for them to send messages to objects on other cards or groups, other stacks in the application, or higher in the Transcript hierarchy.

Like stacks and groups, cards have an inspector window (see Figure 4-4) that gives you some information about them.

Page 98: Software at Speed of Sound -- Dan Shafer

73

Figure 4-4. Card Inspector Window

Fields Fields in Revolution hold information. Everything in a field is stored as styled text.

A field's content is frequently the target of chunking expressions (discussed previously in this chapter). Fields can be any of several types. They also have identifying information associated with them, as you can see in the field inspector window in Figure 4-5.

Page 99: Software at Speed of Sound -- Dan Shafer

74

Figure 4-5. Field Inspector Window

Buttons Many of the scripts you write will be button scripts that activate when the user releases the mouse button. Buttons can be of several types and almost any arbitrary shape. Depending on how they are defined (i.e., their properties), buttons can be push buttons, radio buttons, checkboxes, or menus. (All menus in Revolution are defined as collections of related buttons with a specific property setting that ensures they behave correctly on all platforms.)

Figure 4-6 shows a button inspector window.

Page 100: Software at Speed of Sound -- Dan Shafer

75

Figure 4-6. Button Inspector Window

Images and Graphics Revolution supports two different types of static graphics: images and graphics.

An image consists of a paint bitmap drawn using Revolution’s built-in graphics drawing tool palette or imported from an external graphic file. A graphic is a simple, resizable shape (line, box, circle, etc.) object. In general, use graphics for relatively simple shape objects, and images for more complex pictures such as photographs.

Image objects can display filed images in any of the popular graphic file formats: GIF, JPEG, PNG, BMP, XWD, XBM, PBM, or PGM. While the Apple-proprietary PICT format is also supported, I don’t recommend using it if you aren’t certain that your Revolution stacks will never be expected to run correctly on a non-Apple platform because it’s an Apple-proprietary format that no other operating systems have adopted. Image objects can contain graphics which are either created and stored internally, imported from an external file and stored in the stack, or allowed to remain outside the stack from where they are referenced. In this last case, they are referred to as “referenced controls.”

Both images and graphics objects are scriptable.

Figure 4-7 shows an inspector window for a graphic object, while Figure 4-8 depicts an image inspector window.

Page 101: Software at Speed of Sound -- Dan Shafer

76

Figure 4-7. Graphic Inspector Window

Figure 4-8. Image Inspector Window

Scrollbars

Page 102: Software at Speed of Sound -- Dan Shafer

77

Revolution scrollbar objects consist of a fixed part and a movable part that tracks the relative position or value of an object to which the scrollbar is connected. Scrollbars are perhaps most commonly used to allow the user to move around inside an object or group that is too big to display in the entirety of the area allocated to it on a card.

Some Revolution objects, notably fields and groups as well as some types of players, can have scrollbars attached to them as a property. Such scrollbars are not treatable as separate Revolution objects; only when a scrollbar is explicitly defined by the user or the scripter is it treated as a separate object.

There are actually four sub-types of scrollbar objects in Revolution:

• scrollbar

• slider

• progress bar

• little arrows (often called a “spinner” in other environments)

Figure 4-9 shows a default Scrollbar Inspector window. The popup window is open so you can see the four types of scrollbars. Each of the scrollbar types has its own inspector window layout, as you can see in Figures 4-10 through 4-12.

Figure 4-9. Default Scrollbar Inspector Window With Popup Menu Shown

Page 103: Software at Speed of Sound -- Dan Shafer

78

Figure 4-10. Slider Inspector Window

Figure 4-11. Progress Bar Inspector

Page 104: Software at Speed of Sound -- Dan Shafer

79

Figure 4-12. Little Arrows (Spinner) Inspector Window

Player

A player object is used to display and control an external file stored in Apple QuickTime format. While QuickTime is most often used to create movies, the format is not limited to movies. It can be used for sound, 3-D display and even virtual reality scenarios. A player, like an image, can be either imported into your Revolution stack or referenced from it and stored externally.

Figure 4-13 shows a Player inspector window.

Page 105: Software at Speed of Sound -- Dan Shafer

80

Figure 4-13. Player Inspector Window

Figure 4-14 shows the player inspector window with its popup menu open so you can see the other types of properties associated with a QuickTime player you can modify.

Page 106: Software at Speed of Sound -- Dan Shafer

81

Figure 4-14. Player Inspector Window With Popup Menu Open

Audio and Video Clips

Revolution is quite adept at dealing with multimedia. In addition to the player object discussed in the previous paragraph, it also supports audio clip and video clip objects. These objects are quite similar to one another, differing only by the type of data they contain. An audio clip stores audio data and a video clip, obviously, stores video data.

Neither an audio clip nor a video clip has a representation a card or in a group when you are laying out and designing your Revolution applications. Rather, both are invisible data containers that are manipulated with Transcript commands. A videoclip object appears on a card in response to a command that starts it playing and disappears when the clip is stopped. Audio clips never have a physical appearance, but are played and stopped under Transcript control.

Because they are invisible, audioclip and videoclip objects have no inspector windows.

Page 107: Software at Speed of Sound -- Dan Shafer

82

EPS Object

An Encapsulated PostScript (EPS) object contains text that is stored in the EPS format. This object is only usable on Unix-based systems and has no equivalent on Classic Macintosh or Windows. Macintosh OS X, however, does support Display Postscript, so EPS files are usable in Revolution applications targeted for delivery on OS X.

Page 108: Software at Speed of Sound -- Dan Shafer

83

Chapter 5: Transcript Basics

In this chapter, you'll learn about:

• the mechanics of entering, editing, printing, and managing scripts

• handlers and functions -- their role in Transcript scripting

• messages and how they are passed inside Revolution

• variables and how they are referred to and manipulated

• the special concept of a container and how it is used

• addressing the components of a field in an English-like way

This is a lot of ground to cover, but as you will soon see, learning Transcript is fun and far less difficult than learning any other language you've tried. So get comfortable in front of your favorite computer and prepare to master the basics of Transcript.

Script Mechanics As you already know from our discussions in Chapters 3 and 4, Transcript scripts are attached to Revolution objects. Any Revolution object can have a script associated with it. Any time you edit one of these objects, you can access its script in any of several ways. Figure 5-1, for example, shows the Inspector sub-menu for a Revolution field. As you can see, one of the sub-menu options is “Edit Script.” You can get to the script for any Revolution object by selecting the Edit Script sub-menu item in its inspector.

Page 109: Software at Speed of Sound -- Dan Shafer

84

Figure 5-1. Field Inspector Submenu Showing Edit Script Option

You can look at the scripts of most objects another way. Move the mouse over the object, then hold down the Command and Option keys at the same time and click on the object. In addition, you can open the script editor for the script of the currently selected object by pressing Command-E or selecting the “Object Script” item from the Object menu. There are separate Object menu items for opening the scripts of cards and stacks. Finally, you can edit the script of any object by typing a command into the Message Box that looks like this: edit the script of button “myButton”

Having opened a script-editing window by any method, you can close it one of four ways: (1) by choosing “Close Script Editor” or “Apply Script and Close” from the script editor’s File menu; (2) by clicking in the script editor’s close box; (3) by pressing the Enter key once if the script hasn’t been changed, twice if it has; or (4) by using its keyboard equivalent, Command-W. If you have changed the script since you last saved it, Revolution asks whether it should save the changes before it closes the editing window. You can also choose either the "Apply Script" or “Apply Script and Save Stack” option from the script editor's File menu to keep your changes while leaving the script editor window open. (If you press the Enter key twice in a script editing window after making changes, those changes are accepted with no further intervention on your part. This can be a good or bad thing.)

After you have opened the script-editing window for an object, you will see an empty editing window – unless the object you are editing already has a script, of course.

After you are in a script window, you have the full range of editing capability you'd expect in a basic text editor. You can cut, copy, and paste using the usual command-key equivalents of Command-X, Command-C, or Command-V respectively or you can use the Script editor’s special Edit menu.

One particularly intelligent feature of the Revolution script editor is that it "knows" enough about the programming constructs in the language to handle indentation automatically It places the cursor at the correct position for entering the next line each time you press the Return key. In addition, you can press the Tab key any time and the editor correctly reformats everything in the window.

A side benefit of this capability is that when you press the Tab key, the last line in the handler you are working on is moved flush with the left border of the editing window. If you press the Tab key and the last line of the handler is not flush with the left border, you know then there is something wrong with the script’s syntax.

The script editor's menus When you open the script editor in Revolution 2.0 and later, you'll see a new set of menus on the menu bar (see Figure 5-4). These menus give you full editing control over your script.

The File menu In the File menu (see Figure 5-2), you can save and close scripts as well saving stacks after applying script changes. You can also print the full script (all handlers in the current object script). (At this writing, printing scripts is broken in Revolution 2.6.x. To print scripts, you’ll have to copy and paste them into a text or word processor and print them from

Page 110: Software at Speed of Sound -- Dan Shafer

85

there. Hopefully that problem will be fixed soon.) Notice, however, that you cannot quit Revolution without first leaving the script editor.

Figure 5-2. File Menu in Script Editor

The Edit menu The script editor's Edit menu contains the editing commands you’re familiar with from other programs, but it also adds a bunch more options, as you can see from Figure 5-3.

Figure 5-3. Edit Menu in Script Editor

The word “Objects” in the menu items shown in Figure 5-3 changes to “text” when you have selected text in the script editor.

If you have text in the clipboard, you can paste that text into the script editor as a “formatted string” (which results in it being enclosed in quotation marks), as a comment (in which case two hyphens are prepended to it), or as text.

The Script menu The Script menu in the script editor, shown in Figure 5-4, enables you to manipulate selected portions of your Transcript code.

Page 111: Software at Speed of Sound -- Dan Shafer

86

Figure 5-4. Script Menu in Script Editor

You can select a portion of the script and comment it out with just a single menu command. This comes in handy when you’re debugging your code since code which has been commented out does not execute. Similarly, you can select a portion of the script that has been commented out and uncomment it.

One of the more powerful aspects of the Revolution script editor is the “Insert Control Structure” option from the Script menu. Using this submenu, you can insert If-Then-Else, Repeat, Switch , and Try statements. This saves typing and ensures the correct syntax of these constructs in your code.

(The Transcript editor provides this sort of completion on those control structures as well. Type “repeat” and press return, for example, and you’ll see that the line “end repeat” is placed a blank line below and indented at the same level as your newly typed code line. This helps avoid a common mistake in programming and scripting language: omitting the “end” statement in such constructions.)

The Script menu has two other options: colorize and format. “Colorize” applies colors to key and reserved words in your scripts to make them more readable and easier to debug. We’ll talk more about colorization of scripts in the next section. “Format” has precisely the same effect as pressing the Tab key. That is, it runs a syntax check and, if all is well, aligns the last lines of the handlers in the script to the left margin of the Script Editor window.

Revolution checks your script syntax when you apply it, generating dialog boxes for any errors it identifies.

The View menu The View menu shown in Figure 5-5 contains a number of useful options you develop more and more complex Transcript scripts.

Page 112: Software at Speed of Sound -- Dan Shafer

87

Figure 5-5. View menu in Script Editor

If you highlight a Transcript key word, command, function, or operator and then select the first item in the View menu, Revolution will open a Transcript Dictionary window and display the documentation for that string. If you select this option with no selection or with a selection that is not a Revolution keyword, the system will simply beep or ignore you.

The next group of three menu options affects how the Script Editor window looks and behaves. By default, this window shows all of the handlers in the object’s script in a single pane. However, you can change this behavior to suit your tastes using these three menu toggles. Turning on “Single-Handler View” results in only one handler in the script being displayed at a time. If you choose this option, you should also choose the “Show Handler List” option, without which the single-handler view is less than effective. Figure 5-6 shows a two-handler script without Single-Handler view turned on, but with “Show Handler List” selected. Notice that the left pane displays a list of handlers in the script. The handlers are listed in the order in which they are defined in the script. Clicking on the name of a handler in the left pane selects the first line of that handler in the right pane.

Page 113: Software at Speed of Sound -- Dan Shafer

88

Figure 5-6. Script Editor With Handler List and Multi-Handler View

You can select the “Sort Handler List Alphabetically” option in the View menu to have the list of handler names in the left pane sorted by name rather than by order of definition. In complex scripts, this is often very handy.

Turning on Single-Handler view with the handler list showing produces a result like that shown in Figure 5-7. Notice that the opening and closing lines of the handler are not shown. This allows you to focus solely on the actual lines of code that make up the handler.

Figure 5-7. Single-Handler View With Handler List Visible

Page 114: Software at Speed of Sound -- Dan Shafer

89

Notice the three tabs above the script editing area in Figures 5-6 and 5-7. When you have scripts that are sufficiently complex that they use globals or pass parameters between handlers, these tabs will provide a list of the local and global variables as well as the parameters that are being used in the handlers. For example, let’s define a new handler called multibeep that takes a single parameter, an integer that tells the system how many times to beep. Figure 5-8 shows that a parameter named “x” is being used in the selected handler which, as we can see from the list of handlers, is the multibeep handler. Variables (global or local) and parameters appear in the small editing area above the space where the editor displays the scripts.

Figure 5-8. Script Editor Window Displaying Parameter “x” of Selected Handler

The Find and Replace and Go to Line options of the View menu in the Script Editor are self-explanatory. The Auto-Completion toggle menu item turns on or off your ability to type the first few letters of a Transcript reserved word and then select the appropriate ending from a list that appears in the rectangle beneath the script editing area. Figure 5-9 illustrates this option turned on as you type the characters “se”. Notice at the bottom of the Script Editor you have the option of selecting any of four Transcript keywords that begin with those letters.

Page 115: Software at Speed of Sound -- Dan Shafer

90

Figure 5-9. Script Editor Window Showing Auto-Completion Feature Turned On

You can choose to insert any of these words or phrases into your code by holding down the Command key and pressing the number corresponding to the insertion you want. In Figure 5-9, Command-3 would complete the word “select” in the code.

The next two options in the View menu of the Revolution Script editor determine how searches will work and are largely self-explanatory. I find it most useful to leave Case Sensitive Searching off and Whole Word Searching on, but there are times when you need to alter those settings.

Sometimes scripts can be easier to read and understand if the words that comprise the vocabulary of Transcript are highlighted in some way. It is easier to read a script when colorization of the script words is combined with the indentation provided in the Revolution Script Editor.

The next two items in the View Menu of the Script Editor relate to the colorization of scripts. The first option, “Live Colorization” turns on and off the feature of having colorization take place as you edit your scripts. If you have this option turned off, you can have Revolution colorize your script by choosing the appropriate option on the Script menu, as described in the preceding section. With this option turned on, your scripts will be colorized as you edit them.

By default, the Revolution Script Editor colors language elements as follows:

• commands are colored blue

• control structures are colored brown

• functions are colored dark orange

• property names are colored red

You can change all of these colors by choosing the Colorization Settings… item in the View menu and then selecting each type of language element in turn, clicking the “Choose…” button and selecting a color from the resulting color picker dialog. Figure 5-10 shows the Colorization Settings dialog.

Andrew
Highlight
Page 116: Software at Speed of Sound -- Dan Shafer

91

Figure 5-10. Colorization Settings Dialog in Script Editor

{FIX. Figure 5-10 needs replacing when they clean up the shmutz}

Long lines Because a single command line in Transcript can be any arbitrary length, lines can and often do extend past the right edge of the editing window. Although this does no harm (the scripts still execute), debugging and reading these lines can become difficult. Fortunately, Revolution provides two ways to deal with this problem. If you are entering a line of Transcript code into a script-editing window and want to break the line in the middle for readability, simply insert a backslash character (\) and press the Return key. This symbol is simply a way of notifying Transcript that you haven’t finished the line. You can use this technique as many times in a single Transcript command line as you like. Don’t use it in the middle of a text string enclosed in quotation marks, however, or errors will result.

The other way to keep long lines intact and readable is to choose the “Wrap Long Script Lines” from the View menu in the Script Editor. {FIX - Not working in 2.0 beta}This results in any lines that extend past the right edge of the editing area wrapping back to the left margin. This happens even if the breaks occur in the middle of quoted strings of text.

Comments Even though Transcript is one of the most inherently readable programming languages that exists, comments are still helpful. You can put a comment anywhere in a script, manually or via the Script menu in the script editor. You can indicate comments either with two hyphens (--) or with a pound (or hash) symbol (#).Transcript ignores every-thing that appears to the right of the delimiter. Comments can appear alone on one or more lines or at the end of the line they describe:

-- This comment is the only thing on this line put It -- This comment is on the same line as a co mmand. # If a comment requires more than one line, each li ne of

Page 117: Software at Speed of Sound -- Dan Shafer

92

# the comment must begin with a pound symbol.

Where you have a need for longer comments, it might be useful to place them across multiple lines by starting a block of comments with the character combination /* and ending it with */ as shown in this example:

/* You can begin the comment on the same line as th e delimiter or you can place a carriage return after the de limiter and begin the comment on the next line or any line b elow the delimiter. */

This format gives rise to a common method of providing comments at the beginning of a script or a handler that looks like something like this:

/************************************************* Handler Name: on testTheField Inputs: None Author: Lotta Code Date: Nov. 15, 2004 Version: 0.7b **************************************************/

Revolution also allows you to redefine the default character it uses to delimit comments in your code when you use menu options to comment code segments. Left to its own devices, it will use the double-hyphen. Just select the Default Comment Character item from the View menu. From the ensuing dialog, choose either the double-hyphen or the pound (hash) symbol. Note that when you do this, both symbols remain legal for you to type. You are merely deciding which symbol Revolution should use when it inserts comment symbols in response to your request to do so automatically.

Setting Scripting Fonts You can override the default font, size, and spacing settings, as well as the background color used in your script-editing windows, by choosing the Default Fonts item from the View menu in the Script Editor. Figure 5-11 shows the dialog that results from that choice.

Figure 5-11. Dialog for Setting Font Defaults in Script Editor

Page 118: Software at Speed of Sound -- Dan Shafer

93

As you can see, you can choose new font family, text size, text height (line spacing) and background color from this dialog. Changes take effect immediately and are applied to all open and subsequently opened script editor windows. {FIX new shot when Back Color becomes a button}

Syntax checking The Script Editor does not check syntax when you click the OK button. Syntax and logic errors only show up when you run the script. Other than using the Tab key to confirm that you have matched up beginnings and endings of portions of the handlers correctly, the only way to confirm that a script is correct is to execute it.

Handlers Each script consists of one or more handlers. A handler is a programming construct that begins with the key word on or function and ends with the key word end . Both key words are followed by the name of the message to which the object is being programmed to respond. Commands between the on and end key words are carried out whenever the object receives the message whose name follows them. This portion of the script handles the particular message for which the script is designed, which is why they are called handlers. Any Transcript script can contain one or more handlers depending on the messages, or events, to which each object responds. These messages can be Revolution system messages or messages you create just for your scripts. The key idea to grasp is simply that Transcript scripts do one fundamental thing: they respond to messages. Messages are the actions to which a script must respond. Handlers are the intelligent vehicles for responding to the messages.

Where messages originate We have been talking about messages as if they were all user-generated. Most of the messages for which you will write handlers are a result of the user taking some action. As a result, they handle system messages (see Chapter 6) and have names that identify them as system message handlers. But you can also define your own messages, which then like a function in JavaScript. In this case, one handler generates a message that is dealt with by another handler. This design feature means that you can put frequently needed activities in a single handler and then call that handler from other handlers. For example, you might have a number of handlers that need to ask users if they really want to quit doing some operation. If you define a handler to do this and call it quitOK , you can then call it from inside another handler like this: on mouseUp -- some processing quitOK end mouseUp

Page 119: Software at Speed of Sound -- Dan Shafer

94

A second type of handler Sometimes, you need to define a type of message that can return an answer your script can use. This need is met by the ability to define a function handler. A function handler is similar to a message handler in form , but always includes a special return statement that sends a result back to your handler. For example, you might need to find out the cube of a number in several different handlers. But you need the answer to be returned to you, not just left in the handler that carries out the calculation. The handler to calculate the cube might look like this: function cube num return num * num * num end cube

To use this handler from inside another handler, simply write a line like this: Put cube(5) into theAnswer

You can then take the outcome stored in the container theAnswer and operate on it any way you want.

Messages Without messages, most handlers are irrelevant. Messages are like notices passed from one part of the computer’s system to another. When the user presses the mouse, a message called mouseDown is generated by Revolution. When the user releases the mouse button, a mouseUp message is generated. These messages are sent along a pipeline, to be intercepted and handled by the first object that has a handler with the same name as the message.

Message hierarchy At first blush, it might appear that a message such as mouseUp would be sent to the object in which the pointer was located when the event took place. Most of the time, that’s how Transcript scripts work. But it is not necessary that they work that way. For example, if a mouseUp message occurs on a card, the card may not be designed to respond to such a message at all. But the stack itself might want to do something in response to the user’s release of the mouse button anywhere on the card outside of a button. In that case, the card would not have a mouseUp handler but the stack would. So that Transcript will know where to route messages as they arise, How does Transcript know where to route messages? The language includes a predefined hierarchy through which messages are passed.

Although there are subtle distinctions to be made in special circumstances, you can generally operate on the assumption that messages in Revolution travel a message hierarchy that begins with any scripts that have been defined as frontscripts and continues through the path shown below until they reach the core engine itself, where they are either ignored or generate an error.

From front to back (or bottom to top, hierarchically), the normal message path in Revolution looks like this:

• Frontscripts

Page 120: Software at Speed of Sound -- Dan Shafer

95

• Control (target)

• Group to which control belongs, if any

• Card

• Group defined as having background behavior, if any (may be same as above group)

• Stack

• Inserted stack scripts (aka libraries, inserted with start using command)

• Backscripts

• Externals

• Standalone (if running in standalone mode)

• Engine

Frontscripts, backscripts and externals are special cases which are used by advanced Revolution developers for specific effects. They are well beyond the scope of this book.

NOTE The message path in Revolution is versatile and rich. If you are curious and wish to explore it further, I highly recommend Richard Gaskin’s excellent essay, “Extending the Revolution Message Path” at http://www.fourthworld.com/embassy/articles/revolution_message_path.html.

Messages sent directly to a card, background, group or stack begin their path at the point of invocation and then follow the path shown above.

The three Ms — mouse, menu, and Message box — are the primary sources of user-supplied messages. The keyboard can also be a source of such messages but it generally acts only as a special case of one of the other sources. Messages originating from the user's interaction with the menu bar or the Message box go directly to the group defined as the menubar group. Mouse messages are the most complex of the three basic types of message. A mouse message -- of which there are quite a few -- can be sent to a button (the usual case), a field (provided the field contains locked text), or directly to the card. If the mouse message is generated by mouse activity in a field of locked text, the message is sent to that field first. If it occurs within the borders of a button, that button’s script gets first crack at the message. If it occurs anywhere outside a field or a button, the card’s mouse-message handler (if it has one) is given control.

There is a way to override explicitly the normal hierarchy for sending messages between Revolution objects. The pass and send commands serve this purpose. For the moment, though, ignore this possibility and assume that only the natural order of message-passing is possible in Transcript.

If the first object to receive a message has a handler with that message’s name, it handles the message by following the instructions you put into that part of the script. Once this processing is complete, the message that started it all is “swallowed up” by Revolution and goes no farther unless you force it to do so. However, if the object does not have a

Page 121: Software at Speed of Sound -- Dan Shafer

96

handler for it, then Revolution simply passes the message up the hierarchy to the next level. This process continues until Revolution either runs out of levels or finds a handler.

If a mouseUp message is generated in a locked-text field and that field’s script does not have a handler beginning with the key phrase on mouseUp, Revolution passes the mouseUp message to the currently active card. If the card doesn’t have an on mouseUp handler, it passes the message to the stack. This process continues until some object along the way does have a handler for this message or Revolution itself is reached. (Revolution is also an object in this sense; it is the default destination for all messages.)

The reason a field can only receive a mouse message if it has locked text may be apparent but is worth stating. If the text in a field is not locked, then a mouse click anywhere in the field is the way users show Revolution where to put the next text they type or paste. This mouse-click is not designed to be intercepted, so no handler can be devised to do so. In the above list, you'll notice a reference to Revolution externals. These refer to Transcript’s ability to have its language extended by the addition of commands and functions. Externals are new commands and functions written in traditional programming languages and added to Transcript. The use of external commands is beyond the scope of this book. Do not be concerned about them at the moment except to note where they are in the hierarchy.

User-defined inheritance You can modify the default message-passing hierarchy in Revolution by inserting specific stack scripts or object scripts into the sequence.

With the start using command, you can insert multiple Revolution stacks into the normal hierarchy between your stack and the top level of the hierarchy. These stacks can include any stack contained in any stack file that is loaded into memory. Any message — including, of course, system messages generated by Revolution — that is not handled in your stack will go to the stack scripts in these intermediate stacks before being passed to Revolution itself. You can remove a stack from the message path with the stop using command.

As you can see from the message path list above, scripts in stacks that you start using this way are placed between your stacks and any scripts inserted in the special location called back by means of the backscript command. You can also have Revolution insert the script of a specific object into the back or front of the message-passing hierarchy. Using the insert script command, you can make these scripts available. If you insert them into the special location called front , they will receive messages before the target object is asked to handle them. If you insert them into back , they will get a crack at the messages after the target object and just before Revolution itself receives them. You can return the message path to its original condition by using the remove script command to remove any or all intervening object scripts from the path.

You can use the commands associated with the user-defined hierarchy definition to build libraries of scripts that are useful in certain circumstances. For example, you might have a stack that contains all the scripts that relate to a special high-speed data-locating operation that you need to perform in more than one stack. Then any time you needed

Page 122: Software at Speed of Sound -- Dan Shafer

97

this functionality available, you would simply start using it. When you no longer need that functionality available, issue a stop using command. In both cases, you supply the name of the stack to be inserted into or removed from the message-passing hierarchy.

Variables Transcript has two types of variables: local and global. Unless you specifically declare a variable to be global, Transcript assumes it is local. You can actually define two different types of local variables in Transcript. If you define the variable inside a handler, then the variable’s value is known and usable only inside that handler. If, however, you define a local variable outside the context of any handler, then its value is accessible to and usable by any handler in that object’s script. We’ll see in a moment how to declare and use script-local and global variables.

To define a variable as local to a handler, you don’t need to do anything special. Merely naming and using the variable inside the handler defines its scope to be local to that handler. You can, optionally, use the Transcript keyword local to call attention to the variable and even give it an initial value, but it is not necessary to do so and almost nobody who scripts Revolution uses that keyword for handler-level local variables.

Naming variables There are really only three rules about naming Transcript variables.

• Variable names must not exactly duplicate Transcript reserved words

• Variable names must not begin with a number or special symbol.

• Variable names may not include spaces. Here are some valid Transcript variable names: variablel

tempi

thisIsALongerVariableName

yetAnotherVariable

x

Y

VARIABLES

The Revolution convention of using uppercase letters in the middle of long strings to show where English words would begin is often a good approach to naming variables. Also, case does not matter in variable names. Three variables named This, this, and THIS are all the same variable as far as Transcript is concerned. Because variable names cannot include spaces, quotation marks are never needed around the names of variables. You refer to a variable simply by using its name.

Using variables In keeping with its flexible, forgiving approach to programming language definition, Transcript is "relaxed" about the way you declare and use variables. A variable becomes known to the system the first time you use it. No prior preparation is required. If in the middle of a handler you suddenly need to put some value into a variable, just do it:

Page 123: Software at Speed of Sound -- Dan Shafer

98

put "Beethoven" into composerName

The variable called composerName is now known to Transcript and can be used later in the same handler by just typing its name.

The special variable, It Transcript, in an effort to make scripting more human language-like, includes a highly versatile variable called it that is shared by your scripts and Revolution. This variable lets you write natural-sounding script commands, such as the following two-line combination that retrieves the cantModify property of the current stack and tells the user what it is: get the cantModify of this stack answer “The cantModify property of this stack is se t to “ & it Several Transcript commands -- most notably get -- put their results into the it variable, so you have to remember to be careful when using it in your scripts. Typically, you will use this special variable when few, if any, commands appear between the time you put a value into the variable and the time you need that variable. The following Transcript commands place their results into it by default. (You can almost always supply an alternate destination to avoid overwriting the contents of it if you need to do so.)

• answer

• answer file

• ask

• ask password

• ask file

• convert

• get

• read from file

Don’t put a value into it and then use one of these commands before you have used the original value of it .

Displaying variables To display the contents of a variable to the user, use the put command. This is one of the most frequently used commands in Transcript. In its simplest useful form, the command's syntax is Put expression

where the argument is either a simple source or an expression of arbitrary complexity. In either case, the source or the result of evaluating the expression must produce a string or a number. In this simple form, Revolution places the expression into the Message box. If the Message box is not visible, Revolution automatically makes it visible. The Transcript command line put "This is a test"

will result in the Message box appearing (if it is invisible) and displaying the words This is a test.

Page 124: Software at Speed of Sound -- Dan Shafer

99

Assigning values to variables Another use for the put command is the assignment of a value to a variable. Programming languages such as C and Java, use an equal sign to give a variable a value, such as “start=5”. In Transcript, we use the put command in a more complex form than the one we use simply to display a variable's contents. The form of the put command for variable assignment looks like this: put expression into variable The into preposition causes Revolution to replace the current contents of the target variable, if it has any, with the value of the expression. The following assignment statements are valid in Transcript: put 23 into age put "This is a test" into testMessage put 21 * 2 into x

The expression assigned to a variable may itself be a variable. After assigning 23 to the variable age in the previous example, we could carry out a command like this: put age + 10 into olderAge The put statement can use the target variable as part of its expression and as the assignment destination: put age + 10 into age The put statement can be used with two other prepositions: before and after . These are generally, though not always, used with fields of information on Revolution cards. This use of put is described later in this chapter when we discuss Revolution containers.

Placing a variable’s value into It Because the special local variable It is so useful, there are times when you’d like to place the current value of some variable into It rather than into the Message box, where a simple put statement routes it. To accomplish this, Transcript includes a get command.

The get command has multiple uses. One primary use is with the properties of Revolution objects.

To use the get command, just supply an expression as an argument. Transcript will evaluate the expression and put the result into It . Here’s an example: get age If you carried out this command just after the last use of the age variable above, It would contain the value 33.

Global variables Most of the time, variables are used locally. But when you need carry information from one object’s script to another object’s script, Transcript includes the ability to define variables as global in scope. The global declaration must take place before the variable is used the first time, and it must be made in each handler that uses the global variable. For example, if you want to store the user’s age in a variable and then check the value of age in another button’s mouseUp handler to determine whether a certain action should be taken, then the declarations in the two handlers would look something like this (note that these are both incomplete script fragments): on mouseUp -- Button No. 1's handler

Page 125: Software at Speed of Sound -- Dan Shafer

100

global age put userAge into age

end mouseUp on mouseUp -- Button No. 2's handler

global age if age > 18 then -- do something

end mouseUp

Both handlers declare the age variable to be global in scope. Both handlers therefore "know about" this variable and share its value. Note that if you declared age to be global in Button No. 1's handler but failed to do so in Button No. 2, the second handler would be working with a different age variable than the first handler. In fact, the second handler would have an uninitiated variable called age and the line shown for that object’s script would produce an error because you’d be asking the handler to compare the value of a variable with no value against the known value 18.

Containers A Transcript variable is a special case of a larger class of objects called containers. A container is anything that can hold a value. This includes variables, It, me, target, and special containers related to selected text, the current selection, the Message box, and fields.

Using me and target as containers Generally, me refers to the object whose script is now executing, and the target refers to the object that received the message now being processed. Most often, these are the same object, but sometimes that is not the case. For example, when a message is passed by one object to another, the target continues to refer to the original object that received the message, whereas me becomes the designator of the object that is now processing the message. If me or the target refers to a field, you can use put to alter the contents of the field in conjunction with one of these containers: put "testing 1-2-3" into me put "where are you?" into target

The phrase the target refers only to the object itself, not its contents. Without the word the , the word target refers to the contents of the target when it is a field. That means, among other things, that you may not put values into the target but only into target . This is sufficiently confusing and easy to do wrong that most Revolution developers probably avoid using target as a container.

Selection as a container Text appearing in a field can be the source or destination for put and get commands. If the user has highlighted some text in the field, the highlighted text can be referenced as the selection . If no text is selected, the selection is still recognized by Transcript but is empty. In either case, you can put text before or after the selection or replace the selection with other text using the into preposition. All you have to do is change the

Page 126: Software at Speed of Sound -- Dan Shafer

101

preposition. Figure 5-12 depicts the effects of the put command with its three associated prepositions on the same selected text. In all three cases the text being placed in the sentence is the words “chosen and”. You can see the results of each use of put with a different preposition.

Figure 5-12. The put command and the selection

You can also get the selection, in which case its contents are placed into the special variable It . If you want to store the selection, you can use put with a different container as the destination. Do not confuse the selection explicitly made by the user or your script with text located using a find command. Text found with a find command can be accessed via several functions that act like containers. These are discussed in Chapter 10.

Message box as a container The Message box is also a container. You already know that it is the default destination for the put command. But the Message box can also serve as a place to display information to the user or to send messages directly to objects or to Revolution itself. Like many other Revolution objects, the Message box has a number of aliases. It can be referred to by any of the following names:

• Message box

• message

• msg

• the msg If you use a put command with any of these aliases for the Message box as the destination, Revolution opens the Message box if it isn’t already on the screen and visible. Your program needs to manage the Message box’s visibility only in very unusual circumstances.

Fields as containers

Page 127: Software at Speed of Sound -- Dan Shafer

102

We have already seen how the currently selected text in a field can become the target for a put command. Fields can hold two fundamentally different types of text data: editable text and locked text. (In fact, fields can hold several other sub-types of data, but all are either editable or not based on field-level settings.) If the field holds editable text, the cursor changes to an I-beam when it enters the field’s boundaries. If the text is locked and uneditable, then the cursor becomes the hand. Regardless of editability, you can use the put command to change some or all of the contents of a field. Fields can have names assigned to them when they are created or any time after they are first generated. (We’ll have more to say about addressing fields in a few moments.) Suppose we have a field called Author. Our script has pulled from another stack the information that Asimov is the author of a particular book. Because we’ve discovered the information somewhat automatically, we don't want to require the user to type it into the Author field. We need a slick way of handling this situation. The following command will do nicely. put "Asimov" into field "Author" The into preposition results in the previous contents of the field, if any, be; completely replaced with the new data. Later we find that Asimov’s first name is Isaac and we want to add that information to our growing bits of wisdom. Simple: put "Isaac" before field "Author"

As you would expect, the before preposition places the expression or source data at the beginning of the field named in the command. Similarly, after places its expression or source data at the end of the field named in the command.

Addressing a field's contents Any container, but particularly a field, can hold many words or lines of information. We often want to access specific portions of the contents of such containers. In this area Transcript really shines. It permits us to view a container as consisting of data broken down into items, characters, words, and lines. Furthermore, it permits us to nest addresses so that we can refine the focus of our action as finely as we want. The concept of “chunking,” mentioned in Chapter 4, is at work here. Each subfield we deal with in this discussion is a chunk. Although this discussion could also pertain to variables, we will use the term field to identify the container type in these examples. This is because fields are the most common places to use these addressing techniques and because doing so simplifies the presentation.

Items in a field An item in a field is any string of text found between occurrences of the currently defined item delimiter character. By default, this character is a comma. If only one comma appears in a field, everything to its left is called item 1 and everything to its right is item 2. In a field without commas, only one item exists. Table 5-1 depicts how items are defined and located in a Revolution field.

Table 5-1. Fields and their item components

Field 1 Item address Returned value

Page 128: Software at Speed of Sound -- Dan Shafer

103

This is a test item 1 of field 1 This is a test

A, list, with, commas item 2 of field 1 list

A, list, with, commas item 4 of field 1 commas

Everyone is, a comedian item 1 of field 1 Everyone is

Everyone is, a comedian item 2 of field 1 a comedian

Everyone is, a comedian item 3 of field 1 [empty string]

Lines in a field In many Revolution fields, text occupies more than one line. Sometimes text "runs over" from one line to the next in a field that's really intended to hold just one piece of information (albeit a large one). For example, a field designed to hold your notes about a book in a bibliographic file might occupy several hundred lines of text, but from your perspective, it’s one long field. On the other hand, we often break fields into subfields. For example, a field called Address might hold the street address, city, state, and postal code of people in your address book. If you set the cards up so that the person’s street address is on the first line of the field Address, city on the second, state on the third, and postal code on the fourth line, you have created something similar to an array in other programming languages. (Transcript offers other, more traditional and efficient methods of storing and representing such data, but we will defer discussion of them to Chapter 10 when our focus is on data rather than on fields as containers.) But how would you access the city in such a field? Because no commas are used to separate things, using the item method discussed in the previous section won’t have the desired effect. In this case, you need to focus on a line of information; Transcript permits you to do just that. To get at the city in the Address field, you would simply write a line like this: get line 2 of field "Address"

Similarly, you can place information into a field with the put command using the same kind of addressing scheme: put "Kalamazoo" into line 2 of field "Address"

Characters and words in addresses If Transcript didn’t let you do any more than access data in a single field by its item and line, it would still have more powerful data retrieval capabilities than many traditional data processing programs. But it goes two steps farther. You can access a word or individual characters within a line or field. And you can use the key word to to retrieve ranges of words or characters. To access words, you use the keyword word (oddly enough). To access characters, you can spell out the word character or use the shorthand char . Let’s look at an example, and you will see what we mean about the power in this flexibility of data access. Suppose you have a field called Grades. Stored in each line of that field are the last name, first name, and test scores for students in a class on (what else?) programming Revolution. Figure 5-13 shows a portion of the field, starting with the first line.

Page 129: Software at Speed of Sound -- Dan Shafer

104

Figure 5-13. Field containing student information

A word can contain letters, numbers, or some combination of characters. If you are used to other programming languages, you must keep this fact in mind. There is no need to define a particular chunk of a field or variable as consisting of a particular type of data. Extracting a student’s last name is as easy as: get word 2 of line 2 of field "Grades"

Similarly, getting the grade made by Heather Hunton on the third test requires only that you code a line like this one: get word 5 of line 4 of field "Grades" What if you want to look at the score made by Bill Adams on the third exam to see if it falls in the A grade range? You can extract just the first digit of the grade with a command like this: get char 1 of word 5 of line 1 of field "Grades"

Then you could run a check to see if this first digit is a 9 or a 1, in which an A is probably indicated (assuming nobody scored under 20 on the exam). Let’s change examples. Now we’re working with an inventory stack (a consultant has to be flexible, after all!) You’ve designed the stack so that a part number is stored as a single string of characters in a field called Part No. on each card. The company’s part number design defines the supplier in first three characters, the part number in the next seven characters, and the major subassembly of which this part is a member in the last four characters. You can break this part number into its component parts with some lines of Transcript code that look something like this: put char 1 to 3 of word 1 of field "Part No." into supplier put char 4 to 10 of word 1 of field "Part No." into part put char 11 to 14 of word 1 of field "Part No." int o subAssembly

(As we'll see in a moment, there are some shorthand ways of doing even this powerful addressing. But for now, focus on the use of the to key word to select a range of characters.) You can combine and nest these addressing schemes to as great an extent as makes sense for the data you are managing with your Revolution stacks. In general, it makes the best sense to move from the smallest unit (char) up to the largest (field). Very complex data retrieval is eased greatly with this ability to combine such commands. Take a look at this one: put It into char 3 to 5 of word 2 of item 3 of line 4 of field "LargeField"

We discuss the use of put and get with these complex data retrieval schemes in greater depth in Chapter 10.

Page 130: Software at Speed of Sound -- Dan Shafer

105

Ordinal numbers in addressing schemes The next topic we want to cover in this chapter is the availability of ordinal numbers, which expand shorthand addressing techniques. Ordinal numbers tell order, such as first, second, third. By comparison, cardinal numbers tell amount, such as one, two, three. Revolution makes available built-in labels so that we can access items, lines, words, and characters more naturally than using constructs like "word 1 of line 3." Instead, we can write: first word of third line

Alternatively, for increased readability, you can add “the” judiciously:

the first word of the third line

Revolution defines ordinals for the numbers one through ten (first through tenth), as well as the following special ordinals: • last • mid or middle • any (one picked at random) We find last particularly useful. In an address field, for example, we might not know how many names precede the person's last name (depending on things such as whether a title is used and how many middle names or initials the person has). That would drive some database programs insane. But with Transcript, we simply code something like this: get last word of line 1 of field "Address"

We know that the last name is the last word on the line, regardless of how many words precede it. Remember, too, that all we have said about addressing fields of data applies equally to variables, It , the Message box, and the selection.

Page 131: Software at Speed of Sound -- Dan Shafer

106

Chapter 6: System Messages In this chapter, you'll learn:

• what a system message is

• how to choose the destinations for system messages

• how to use all the system messages generated by Transcript

• how to transfer messages from their default destinations to other target objects in the Revolution environment

Messages Galore! There is always something going on in Revolution. Even when it doesn't look like there's anything happening, Revolution is sending a constant stream of messages to objects in its environment. If a script isn't active and sending messages of its own and if the user isn't doing something to generate a specific message, Revolution sends out a continuous stream of messages to let the objects in its hierarchy know that nothing special is happening.

Your computer’s operating system, quite apart from Revolution, is an event-driven environment. The Transcript equivalent of an event is a message. There are only two sources of messages in Revolution: a script running as part of a stack and Revolution itself. Messages originating with Revolution are called system messages. In this chapter, we look at all the system messages Revolution generates. A large percentage of your Transcript programming is devoted to responding to or monitoring these messages. Before you read this chapter, make sure you thoroughly understand the concepts of messages and handlers, as discussed in Chapter 5.

Who Gets the Message? One of the most important ideas to grasp early in our discussion of system messages is that every system message has a default destination to which it is automatically sent. As we cover each system message in this chapter, you will see the logic of choosing the default destination for each such message. Revolution routes some messages to the object in which the event with which they are associated takes place. Others are sent to different Revolution objects depending on the state of the system. Still others have default destinations that are not dependent on any outside factor.

Every default destination can be overridden in your scripts. If Revolution sends a message by default to the stack, for example, and you want to override the stack's handling of that message in some circumstances, simply design a handler with the same name in your script and intercept the system message.

This flexibility applies to messages, not to built-in system functions and commands. These are always routed directly to Revolution without any intermediate stops to see if

Page 132: Software at Speed of Sound -- Dan Shafer

107

you’d like to handle them differently. For example, the answer command with which you are already familiar is a built-in command. You cannot create an on answer handler in one of your scripts and expect to intercept that message and process it differently from the way Revolution is designed to handle it.

Using send to direct a message You can also override Transcript's normal message-passing hierarchy, which we studied in some detail in Chapter 3, by using the send command. The basic syntax of this command is:

send <message> to <object>

For example, if you want to simulate the user clicking a mouse on a particular button, you can do something like this:

send mouseUp to button "Test"

Using pass to avoid message disappearance As we pointed out in Chapter 5, whenever a message traveling along the Transcript message-passing hierarchy encounters a handler of the same name, it executes that handler and then disappears from the message stream. This is not always desirable.

For example, if you write a handler to intercept a system message and you want to do some special processing of that message but then permit Revolution to add its own standard functionality as well, you need to make sure Revolution ultimately gets the message. To do this, use the pass command. Its syntax is simple:

pass <message>

The message being passed must be the one the handler is processing. In other words, you can't pass a mouseUp message from an openCard handler. Here's what a keyDown handler might look like if you needed to process some keyboard activities within a custom handler but wanted Transcript to deal with all other key processing:

on keyDown theKey if theKey is “j” then --special processing for your handler end if pass keyDown end doMenu

You should be cautious about writing handlers to intercept and manage system messages. If you do so, you assume all the responsibility for making sure the system reacts appropriately to the message. In some cases, this involves understanding Revolution at a very deep level.

Page 133: Software at Speed of Sound -- Dan Shafer

108

An Overview of System Messages System messages usually contain information about the status of some portion of the system or your application at the time the messages are generated. In general, they result from the user taking some action that leads directly or indirectly to them being generated. There are a number of ways system messages could be categorized. In this chapter, I’ll organize my discussion of system messages as follows:

• user interaction

• control management

• Revolution-related

Each of these categories of messages is discussed in the following sections, where I provide an overview of the scope and nature of messages contained within each one. My purpose here is to give you a sense of the broad sweep of messaging that goes on inside Revolution. Later in the chapter, I’ll describe some of the basic techniques for handling representative messages in each category. More detailed information about handling specific messages is available both through the Revolution online documentation (specifically the Transcript Dictionary which is, for the most part, comprehensive and quite useful) and in the Transcript Vocabulary Database at the Revolution Pros Web site companion to this book.

User-Interaction Messages

User-interaction messages are those messages generated by the user with the mouse, keyboard, or menu selections. I’ll lump the help message into this group as well, though it is not specifically linked to a single user action.

Mouse messages Revolution can generate 11 mouse messages. Eight are actually button messages because they report the status of the mouse button. The other three are location messages that tell your script where the mouse is with regard to specific objects. (There are several built-in functions in Transcript to help you pinpoint the exact coordinates of the mouse without regard to objects. These are discussed in Chapter 7.)

Mouse-button messages are among the most frequently used in Transcript scripts because it is often important to know when and where the user has activated the button. The mouse button is the user's primary means of interacting with a Revolution stack.

Keyboard messages Another group of 21 messages reports the pressing of keys on the keyboard, including special keys. Four of these messages let you intercept and deal with any key the user

Andrew
Highlight
Andrew
Highlight
Page 134: Software at Speed of Sound -- Dan Shafer

109

presses. Ten other messages permit you to detect when the user presses one of the following special keys: Return, Enter, Tab, Command (Control), Option (Alt), Control, Backspace, ESC (Escape). Yet another message informs your script if an arrow key has been pressed and, if so, which of the four arrow keys has been used. Another system message tells you which function key has been pressed.

Four messages allow you to deal with the various key combinations used on different operating systems to carry out cut, copy, paste, and undo commands. Finally, there are two messages whose purpose is to allow you to intercept the use of either the return key or the enter key when the cursor is placed within a field.

Help-management message One message can be discussed under the rubric of housekeeping. It permits you to intercept the user's request for help via a menu selection or the keyboard.

Menu message One system message relates to menu activity. The menuPick message allows you complete control over user interaction with menus in your application, extending even to hierarchical menus.

Drag-and-Drop messages Five different Revolution messages are involved when the user drags an object inside a Revolution application and moves it elsewhere on the card. These are:

• dragEnter

• dragStart

• dragMove

• dragEnd

• dragDrop

Player interaction messages One of the most versatile objects you can use in your Revolution applications is a player. A player can be used to display and control a movie or a sound file. (Note that in Revolution 2, it is recommended that you use audioClip and videoClip objects sparingly, preferring the player object instead.) There are three system messages that relate to all players. These are: playPaused , playStopped , and currentTimeChanged . Two others -- nodeChanged and hotSpotClicked are used only with Quick Time Virtual Reality (QTVR) players.

Page 135: Software at Speed of Sound -- Dan Shafer

110

Focus and selection-related messages Four Revolution messages deal with the user’s interaction with objects that can either receive keyboard focus or that have contents that can be selected and modified. Two of these -- focusIn and focusOut -- provide you with a way in your scripts to intercept the user’s movement among objects in your application’s windows. One of these messages, selectionChanged , allows you to detect when the user has selected some text in a container (typically a field) and changed it. The fourth message is linkClicked ; it’s generated when the user clicks on a piece of grouped text. I’ve lumped it here because grouped text and the user’s interaction with it is more like the process of dealing with text in a container than it is like most mouse interaction.

Object movement and sizing messages If you allow users to move or resize windows (stacks) or components as they run your Revolution applications, you’ll want to be aware of six system messages connected to these operations. Two of these messages -- moveControl and moveStack -- are generated when the user moves a control or the stack window. Similarly, resizeControl and resizeStack are sent when the user resizes a control or the stack window. The other two messages -- iconifyStack and uniconifyStack -- get triggered when the user collapses (Mac OS systems), iconifies (Unix systems), or minimizes (Windows systems) the stack window.

Messages related to starting and stopping

If the user creates or deletes an object, or opens or closes an object, Revolution sends a system message to inform your script (and the rest of the system) of the event. Not all objects in Revolution can be opened or closed; in the case of such objects, there is no corresponding message.

This category has the largest number of system messages. Such messages are generated in response to the following broad categories or types of events:

• drag-and-drop activity

• moving an object

• opening an object’s script for editing

• resizing stacks or objects

• shift in the input focus

• ID or name of an object being altered

• preparing to open a card, stack, or group with background behavior

• changes in stacks comprising the message-passing hierarchy

• manipulation of a scrollbar

• iconifying and un-iconifying a stack

• saving a stack

Page 136: Software at Speed of Sound -- Dan Shafer

111

Revolution-Related messages

There are three categories of message related to Revolution (and, by extension, your application) itself. Several of these messages involve the startup/shutdown/suspend/resume cycle of stack usage. Another set relates to error messages and dialogs and how they are handled. The final one is the idle message.

Handling system messages

The rest of this chapter is devoted to a more detailed look at how you write Transcript code to handle the most important and often-used system messages. Along the way, I’ll provide some basic examples of the use of Transcript to intercept and respond to these messages.

Many subsequent chapters dwell in greater depth on the commands, system messages, properties, and management of specific types of controls. The purpose of this chapter’s discussion is to provide you with a basic level of comfort with the process and a fundamental understanding of some of the more common messages so you’ll have a framework for the later chapters.

Handling Mouse Messages We will first look at mouse-button messages, handlers for which form such a major part of any Transcript script. Then we'll examine mouse-location messages, which are used less frequently but with which you should be familiar.

Handling mouse-button messages When you click the mouse on a button in any application, whether it is written in Transcript or a more conventional programming language, you probably think of the task as consisting of one or two steps. Most users think of it as one action called clicking the mouse button. An astute observer might point out that there are actually two events taking place: pressing the mouse button and releasing it.

Revolution views this mouse action as consisting of three separate events, and it generates a system message for each. These events and their associated system messages are:

• the pressing of the mouse button, which generates a mouseDown message

• the continued holding down of the mouse button, however briefly, which generates a mouseStillDown message (This message has been deprecated in Revolution 2.x, which means you should not use it.)

Andrew
Highlight
Page 137: Software at Speed of Sound -- Dan Shafer

112

• the release of the mouse button, which generates either a mouseUp message or a mouseRelease message depending on where the user releases the mouse button with respect to where it was first pressed

The user's mouse-button activities are most often of interest when they take place inside buttons. That is why most mouseUp handlers occur in button scripts. But you can supply handlers for any of these messages in a field with locked text or even in a card or stack. They are also frequently useful inside other interactive objects, notably scrollbars.

There are some important rules to learn about how Revolution processes mouse clicks. First, the mouseDown message is sent to the object in which the mouse pointer is located when the mouse button is pressed. Second, all subsequent mouseStillDown messages are sent to the same object that received the mouseDown message even if the mouse pointer moves outside that object while the button is depressed. Finally, a mouseUp message is sent by Revolution only if the mouse button is released within the confines of the last object to receive a mouseDown message. If the user presses the mouse button in, for example, a button labeled OK, and then drags the mouse outside that area and releases the mouse button, no mouseUp message is generated. Instead, a mouseRelease message gets sent so that you can respond to the user clicking inside an object and then dragging the mouse outside the object and releasing the mouse button. This design is in keeping with expected button use, which permits users to change their minds any time before releasing the mouse button.

You will seldom, if ever, write handlers for mouseDown or mouseStillDown . But mouseUp handlers are among the most common in Transcript scripts. Virtually every button has such a handler.

To demonstrate how these three commands relate to one another, let's create a Laboratory stack. We will use this stack repeatedly through the book. (It is included in the SmartEBook™ version of this book via a button on the stack from which this book is opened.) Start with a simple, blank stack. Add a new button and a new field to the first card. Name the field “trackField”. Now we'll put a handler for each type of message into this button's script.

Laboratory: Four mouse-button handlers

Here are step-by-step instructions for this experiment in button scripting:

1. Open the button's script editor. 2. Type the following script, which consists of three handlers. When you've

entered and proofread it, click the Apply button in the script editor.

on mouseDown put "Down" into field trackField wait 40 put 0 into field trackField

Page 138: Software at Speed of Sound -- Dan Shafer

113

end mouseDown

on mouseStillDown add 10 to field trackField end mouseStillDown

on mouseRelease put “Changed your mind, eh?” into field trackField end mouseRelease

on mouseup beep 3 wait 20 put "Done!" into field trackField end mouseUp

1. Put the pointer over the button, then press and hold the mouse button. The word “Down” appears in the field. Hold the button down for a few moments and the word "Down" is replaced by a series of rapidly increasing numbers.

2. Release the button. In a moment, the word "Done!" appears in the field. 3. Repeat step 3 but after you press the mouse button inside the Revolution button,

drag the pointer outside the button area. Notice that the numbers keep increasing, indicating that the mouseStillDown handler in the button's script is still receiving messages. Now release the button outside the button's area. The message “Changed your mind, eh?” appears in the field and the counting stops. But notice that no “Done!” message appears and no beeps are heard because the mouseUp message was never sent.

There are four other system messages related to mouse-button activity for which you will almost certainly find less use.

The mouseDoubleDown and mouseDoubleUp messages give you a chance to respond to double-clicks of the mouse by the user. Generally, you’ll want to write a handler for the mouseDoubleUp message. The mouseDoubleDown message is only sent while the mouse is down or held down, which is a relatively momentary event.

The other two messages -- mouseDownInBackdrop and mouseUpInBackdrop -- are useful only when you create a kiosk-style application where you define a backdrop property for your application that obscures all other applications and the desktop. Creating handlers for these messages will enable you to respond to the user clicking outside the confines of the application window.

All of these messages can have an optional parameter that is a number from 1 to 3 defining which specific button is being dealt with. If this parameter is not included, it is assumed to have a value of 1. Here are the meanings of the three numbers on various operating systems:

Page 139: Software at Speed of Sound -- Dan Shafer

114

• 1 is the only Macintosh button; left button on Windows and Unix systems

• 2 is the middle button on a three-button mouse

• 3 is the right button on Windows and Unix systems, simulated by Control-click on the Macintosh

Handling mouse-location messages Three messages define where the mouse is when the mouse button is not being pressed. They relate the position of the mouse to objects such as buttons and fields. The three messages and their meanings are:

• mouseEnter , indicating that the mouse pointer has entered the boundaries of and object

• mouseLeave , indicating that the mouse pointer was in the boundaries of an object but has now moved outside those boundaries

• mouseWithin , indicating that the mouse pointer has entered the boundaries of an object and remains there (This message has been deprecated in Revolution 2.x, which means you should not use it.)

Revolution interleaves the mouseWithin message with the idle message any time the pointer is located inside an object’s boundaries. Mouse-location messages are handled exactly like the mouse-button messages described in the preceding section.

One interesting use for mouse-location messages is the creation of "pop-up" fields that appear when the user simply moves the mouse pointer to a certain area of the screen. Such a handler might be quite useful, for example, in an educational script. You could provide a picture of a part of the human body with no visible labels. When the user points the mouse at a specific location, a previously hidden field appears, showing the user the label for that location. When the user moves away from that area, the field goes away. The user can scan the entire set of labels with the mouse easily, without ever pressing the mouse button.

Another common use for the mouse location messages is to create “rollover” buttons which change color or text or size when the mouse is positioned over them. This type of button, popularized by Web designers, have become all but de rigueur in application design.

Although developing such an application is beyond the scope of simply learning about these handlers, I can give you something of the flavor of such a script with the following laboratory experiment.

Page 140: Software at Speed of Sound -- Dan Shafer

115

Laboratory: Popup field controlled by mouse

Open the Laboratory stack again if it isn't already open or create a new one. (You can also open Chapter 6, Lab 2 from the SmartEBook.)

1. Create a new card and with one field. Give the field the name “Test Field”. Then

put some arbitrary text into it.

2. Create a new button. Enter the following script into its script editor:

on mouseEnter show field "Test Field" end mouseEnter on mouseLeave hide field "Test Field" end mouseLeave

2. Be sure the field Test Field is invisible. You can do this one of two ways: by

unchecking the checkbox next to its Visible property in its inspector, or by typing into the Message Box:

hide field "Test Field"

Before the experiment starts, this laboratory card should look similar to Figure 6-1. Move the mouse pointer over the button to which you've added the script. (We've called our button, unimaginatively enough, Test Button.) The field should suddenly appear, as shown in Figure 6-2. As you move the pointer outside the button, the field disappears again.

Figure 6-1. Mouse-location test before pointer moves

Page 141: Software at Speed of Sound -- Dan Shafer

116

Figure 6-2. Mouse-location test with pointer over test button

Handling keyboard messages You can trap the user's keystrokes with the keyDown message. This message is generated when the user types any alphabetic or numeric key without any modifier (Command, Option, Control) key being held down at the same time. The message is accompanied by the name of the key being pressed and may also be trapped. Here, for example, is a handler that will display in the Message box each key the user types, replacing the Message box's previous contents as it does so:

on keyDown key put key end keyDown

If you put this handler into the stack or card script, and then ensure that no field or button has the focus, you’ll see that when you press a key on the keyboard, it shows up in the Message Box. If the Message Box is invisible when you press the first key, it will appear.

The Tab, Return, Enter, Command, Option, Backspace, and arrow keys generate their own messages when they are pressed anywhere except in a text field. The function and Escape keys produce their messages even inside a text field, but different keyboards and operating systems require different user actions to invoke the Function keys. For example, on some Macintosh keyboards, the use has to hold down a special key (labeled “fn”) to activate Funciton Keys 1 through 4 or 6. Those keys when pressed without the fn key being depressed have special meaning to the OS.

You can cause the Return and Enter keys to be recognized when they are pressed while a field container has the focus by using the enterInField and returnInField messages.

If you want essentially to take control of the keyboard regardless of what, if any, component might have the focus, you can use the rawKeyDown and rawKeyUp

Andrew
Highlight
Page 142: Software at Speed of Sound -- Dan Shafer

117

messages. These messages pass along the numeric key code for the key the user pressed. Each key on a keyboard has its own unique key code. If you don’t pass the code up the message-passing hierarchy, it is effectively swallowed up by your handler and nothing happens.

NOTE: If you don’t know the key code for any key, just create a simple handler that displays the code. Such a handler might look like this:

on rawKeyDown theKey put theKey end rawKeyDown

The possible uses for handlers for these messages are not as clear as the uses for mouse messages discussed in the previous section. But some potential uses are hinted at in this discussion.

These system messages are related to special keys:

• tabKey • returnKey • enterKey • functionKey number • arrowKey direction • controlKey number

• controlKeyDown number • commandKeyDown key

• backspaceKey

• deleteKey

• escapeKey

• optionKeyDown key

Revolution allows you to generalize the handling of the cut, copy, paste, and undo editing operations across the platforms on which it runs. You can use cutKey , copyKey , pasteKey , and undoKey , to trap these keyboard combinations and handle them differently from the way the OS handles them.

Most of these messages are sent to the currently active control or to the current card if no control is active.

Laboratory: Key-button equivalents

Andrew
Highlight
Page 143: Software at Speed of Sound -- Dan Shafer

118

One interesting use for message handlers for the Return and Enter keys is to provide a keyboard equivalent for some button selections. Let us assume we have a card with two buttons, one marked OK and one marked Stop. Let's further assume that in documentation or in onscreen help (or, better yet, in both places) we have told users that they can press the Return key instead of clicking the OK button and that pressing the Enter key is the same as clicking the Stop button. (Note that if a pushbutton is defined as being the default button, we don’t need to write a handler to cause it to be invoked when the user presses the Return key. This exercise assumes that we haven’t defined a default button.) All we need is a card script that passes these messages to the appropriate button targets when they are received.

Here are the step-by-step instructions for this laboratory experiment in using the returnKey and enterKey system messages to create keyboard equivalents to button-clicks.

1. It is more efficient to create a new card in the laboratory, though you can go through the process of cleaning all the leftover buttons and fields from the present card if you like. We will not be returning to the previous examples, so choose the alternative you prefer. In any event, it is a good idea to begin this experiment with a clean slate with no handlers or objects.

2. Create two buttons. Size, type, and location are immaterial. Name one button OK and the other Stop.

3. Enter the following script into the editor for the OK button:

on mouseUp put "OK button selected" end mouseUp

4. Enter the following script into the script-editing window for the Stop button:

on mouseUp put "Stop button selected" end mouseUp

5. Enter the following two handlers into the card's script:

on returnKey send “mouseUp” to button "OK" end returnKey on enterKey send “mouseUp” to button "Stop" end enterKey

Andrew
Highlight
Page 144: Software at Speed of Sound -- Dan Shafer

119

6. Select the Browse tool. Click on the OK button and confirm that the appropriate message appears in the Message box. Then try the Stop button.

7. Click the stack window again and then press the Return key. The message "OK button selected" appears in the Message box. Now press the Enter key. The message "Stop button selected" appears in the Message box.

Notice that we did not send the returnKey and enterKey messages themselves to the buttons. Rather, we sent messages that the buttons already had in their scripts. This is not only permissible, it is the most efficient way of handling the task as long as the actions are the same when the user pushes the button with the mouse pointer or uses the keyboard equivalent. You can, however, send the keyboard message on to another object directly. If, for example, you want to highlight the OK button when users press the Return key but not if they use the mouse, you need a handler in the OK button script for the returnKey message. You could then simply pass the returnKey message to the button from the card script. The other keys operate in an analogous manner.

Special field-related keyboard messages When the user presses the Enter or Return key inside an editable (that is, unlocked) field, Revolution generates the enterlnField or returnlnField message, respectively. These messages enable you to treat these keys as something other than simple carriage returns to be entered into the field. We'll have more to say about using these system messages in Chapter 10 when we discuss managing text and data in Revolution fields.

Uses for keyboard messages We have already discussed how keyboard messages can be made to work as keyboard equivalents of button presses. The same technique can be used for other actions that have keyboard equivalents.

The arrow keys lend themselves particularly well to a different usage. Because they are usually seen by the user as navigation keys, you can intercept these keys to control navigation in ways that make the user's interaction with the stack more understandable. For example, the right arrow key (message arrowKey right) can be intercepted at the group or stack level and interpreted to mean "go to the next card with this group." In multiple-background stacks, this can be quite helpful.

The keyDown message can be used to create interfaces in which the user need only press a single key to indicate a desire to do something or an answer to a question. This might come in particularly handy in creating educational stacks for young users for whom the mouse might require too much fine-motor skill or for fast-action interactive games where key presses are more efficient ways for users to direct the game’s action.

Page 145: Software at Speed of Sound -- Dan Shafer

120

You can use the commandKeyDown message to intercept user attempts to bypass your control of menu interaction by pressing the Command key and a keyboard equivalent (for example, Command-Q for Quit). In that case, you probably want to use the pass command to make sure that any Command-key combinations for which you do not wish to trap are handled in the way the user expects them to be handled. It is important to note, too, that the commandKey and commandKeyDown messages, which are Macintosh-specific, are equivalent to controlKey and controlKeyDown messages on Unix and Windows systems. On the Macintosh, however, controlKey is the Control Key; it is not merely a synonym. In fact a handler for either Command key message will be responded to on Windows and Unix systems as if a Control key message had been sent. The messages are synonyms.

Note, too, that on a Macintosh, none of these messages is sent unless the user holds down another key while pressing the Command key.

Handling the Menu-Related Message

Revolution implements menus as stacks of connected buttons. The button’s menuMode property determines how the button behaves. It can be any of the following: pullDown , cascade, popup, tabbed, comboBox, or option.

When the user selects an option from any menu, Revolution generates a menuPick message with a parameter that is the text of the menu item chosen. If the item is part of a submenu, the name of the item will start with the name of the submenu and separated from the item name by a vertical bar. For example, if the user selects the “Bold” option from the “Style” submenu of the “Font” menu of your application, the message sent by Revolution will be: menuPick Style|Bold.

If you hand-code menu scripts as opposed to using the Revolution Menu Manager, you’ll write an on menuPick handler with a case statement to deal with the various menu items and submenu items.

Handling the help Message The help message is created and sent to the currently open card when the user requests help by pressing the F1 key (or other designated help key depending on OS and keyboard combinations).

The help message can be intercepted by your script to route the user's request for assistance to a special stack (or part of a stack) where the user can get customized help or specific references to other sources of assistance. For example, if you have a card called Special Help that contains hints for using your stack, you can send the user there when he or she asks for help. The handler would look something like this:

Page 146: Software at Speed of Sound -- Dan Shafer

121

on help go to card "Special Help" end help

(In reality, the handler would be a bit more complex because it would involve making sure Revolution returns to this point when it finishes furnishing the specialized help. The techniques for doing that are covered in Chapter 9.)

Handling Control Management Messages As I said earlier, action-taking messages fall into two categories. The first group, deals with the management of specific objects in the Revolution environment. The second is related specifically to Revolution's own operation.

Object-related action messages Users can open, close, create, or delete objects manually and you can do those same things in scripts. Whether the actions occur as a result of the user's instructions or a script's execution, your script may want to intercept the actions by providing handlers for the appropriate messages.

Fields, stacks, cards, and groups (aka backgrounds) can be the recipients of all these messages. Buttons can be created and deleted but not opened or closed. By combining the first part of the message name — the action to be taken — and the second part—the type of object to be affected — we come up with a matrix like Table 6-1, which lists all these common system messages by function.

Table 6-1. Action-taking system messages

Action to Take

Background Button Card Field Stack

Create object newBackground newButton newCard newField newStack

Delete object deleteBackground deleteButton deleteCard deleteField deleteStack

Open object openBackground NA openCard openField openStack

Close object closeBackground NA closeCard closeField closeStack

Generally, these messages are sent at a time that makes their interception meaningful from a scripting perspective. For example, the closeBackground message is sent to a card when the user or a script is opening a new card that does not contain the same group as the current card. It is sent before the new card opens so that if you wish to do something different in this case from what you do when the background continues to be the same, you can do so.

The object-creation system messages are sent as soon as the object is created and they are sent to that object. Thus if a script or the user creates a new field and there is a newField

Page 147: Software at Speed of Sound -- Dan Shafer

122

handler in the currently executing scripts, that field will execute that handler as soon as it is created. One implication of this is that you can’t use these system messages to prevent the creation of new objects. (You could, of course, send an immediate delete command to the object and effectively prevent its addition to the application if you wished to do so.)

There is one additional message that should be discussed in this context of opening, closing, creating, and deleting objects. The closeStackRequest message is sent when the user attempts to close a stack. If you want to prevent the user from closing your stack, intercept this message in an on closeStackRequest handler and take whatever action is appropriate.

In addition to the four basic types of controls, the following controls will also generate appropriate messages when they are being created or deleted. Like the button, however, they do not have equivalent open and close messages:

• EPS (encapsulated PostScript)

• Graphic

• Image

• Player

• Scrollbar

Interestingly, there are also newGroup and deleteGroup messages which have the same functionality as the newBackground and deleteBackground messages, but no equivalent open or close messages that use Group instead of Background. This feels to me like a syntactical oversight on the part of Revolution’s developers, but it’s at worst a minor annoyance. A closeField message is generated when the user changes the contents of an editable text field and then removes focus from the field. Your script can intercept and handle this message, as discussed in the next section. If the user removes focus from a field without having changed its contents, Revolution generates an exitField message.

Before the appropriate open message variation is sent for a stack, card, or background, Revolution first issues a pre-opening message named, respectively, preOpenStack , preOpenCard , and preOpenBackground . These messages give you an opportunity to do some special processing before the newly opened stack, card, or background appears visible on the screen.

Typically, your scripts will intercept messages that result from the user taking actions that generate object-oriented action messages and will change the usual manner in which Revolution processes them. Also typically, your scripts will work with the opening and closing of objects.

Page 148: Software at Speed of Sound -- Dan Shafer

123

Laboratory: Handling a newCard Message

In this experiment, we set up a script that automatically inserts information into any new card created by the user. It's a good idea to start with a new card in the laboratory.

1. Create a field in the upper-left corner of the card. Define it to be a rectangle and give it the name Today. Give it any other nonconflicting characteristics you wish.

2. Make the field a group by choosing “Group Selected” from the Object Menu. Give the group a name like “Group1.”

3. In the Group inspector, check “Behave like a background.”

4. Open the stack's script.

5. Enter the following script for the stack:

on newCard put the date into field "Today" end newCard

(The built-in function called the date simply returns today's date in short format.)

4. Return to Browse mode. Select New Card from the Edit menu or hold down the Control or Command-N keys. Add a few more cards if you like (keeping track of how many there are so you can undo the work later) and confirm that each displays today's date when it is created.

5. Delete the cards you created and the new background you generated to avoid confusion with future laboratory experiments. If you want to keep them, make a copy of the stack using the Save a Copy... option from the File menu and then delete the new background and cards.

Stack Window Manipulation Messages If the user moves or resizes the stack window of your application, Revolution generates either a moveStack message or a resizeStack message. These messages are unusual in that they pass along parameters providng you with the new location or rectangular size of the stack window. They are sent only after the event is complete.

The messages iconifyStack and unIconifyStack when the user collapses and expands, respectively, the stack window. The terminology for each of these actions is system-dependent but the effect in all cases is the same.

Resizing and Moving Controls

When the user (but not a script) either resizes a control or moves it, Revolution generates appropriate system messages. In a compiled Revolution application, your user will typically not be able to do either of these things. You can, however, script your

Page 149: Software at Speed of Sound -- Dan Shafer

124

applications to permit such actions. Or you may be delivering your application to users in an uncompiled format where they have access to Revolution themselves.

In any case, if the user is permitted to move or resize individual controls, Revolution sends a moveControl or resizeControl message after the move is complete. You can then rearrange things on the card, more precisely position the relocated control, or take whatever other action seems appropriate.

As you gain more experience with Transcript scripting, you'll find many occasions for the use of these object-related messages.

Handling Focus and Selection-Related Messages One of the central ideas in modern GUI-based applications is the concept of focus. A control is said to have the focus when keyboard activity would be directed to it. A great many commands are related to identifying and retrieving the current selection, which at any time can be an object or the contents of an object (particularly a field).

But focus and selection are also responsible for generating four different system messages:

• focusIn

• focusOut

• selectionChanged

• linkClicked

The focusIn message is sent to an object when it becomes the focus of keyboard events. If the control is an unlocked field or a button whose menuMode is “comboBox”, the openField message is sent to it instead of the focusIn message.

Similarly, the focusOut message is sent to an object (button or field) when it becomes inactive and loses keyboard focus. Again, fields are an exception: an exitField or a closeField command is sent to a field depending on whether the user has changed any of the field’s contents.

The selectionChanged message is sent to a field when the user moves the cursor in the field, thereby changing the text that is currently selected. The message is sent to a player object when the user selects a sound or movie segment and when a script changes the start or end point of a player segment.

When the user clicks on text in a text group, Revolution generates the linkClicked message. This message passes a parameter to any interested handler. The parameter’s value depends on the linkText property of the grouped text being clicked. If the clicked

Page 150: Software at Speed of Sound -- Dan Shafer

125

text is grouped text—that is, if its textStyle property contains “link”—then the linkText property of the clicked group is sent as the linkedTextOfChunk parameter. If the text’s linkText is empty, the clickText is sent as the linkedTextOfChunk parameter.

Handling Drag-and-Drop Related Messages

Revolution allows you to implement drag-and-drop in your applications by generating a series of six messages you can intercept and handle in your scripts:

• dragDrop

• dragEnd

• dragEnter

• dragLeave

• dragMove

• dragStart

These messages are pretty self-explanatory. When the user clicks on an object, the dragStart message gets sent. (The object must be one that you’ve defined to support drag-and-drop.) As the user drags text or an object, Revolution issues dragMove messages each time the mouse moves with the object still being dragged (i.e., the mouse remaining down). When the user drops the dragged object in a new location, the dragEnd message gets sent to the object from which the drag-and-drop operation initiated, and the dragDrop message is sent to the object receiving the dropped object.

Supporting drag-and-drop is more complex than I’ve described here. It involves setting properties of the objects involved in the drag-and-drop process, for example. I’ll cover drag-and-drop of text and data in Chapter 10.

By the way, you don’t need to write any code at all to handle drag-and-drop of text between two unlocked fields. This functionality is an integral part of Revolution and requires no scripting on your part.

Page 151: Software at Speed of Sound -- Dan Shafer

126

Handling Messages Related to Starting and Stopping

When your application opens, Revolution sends a startup message that you can handle to undertake special processing that needs to be done when your application begins. If your application contains multiple stacks, the startup message is sent to the first stack that gets opened by the application process.

At the other end of your application’s life cycle, the user can quit the program. This will trigger a shutdownRequest message, which you can handle to ensure an orderly shutdown of the program or to interrupt and abort the shutdown process completely. If you don’t handle the shutdownRequest message, then the shutdown message is issued. You can intercept this message as well, but doing so and not passing it up the hierarchy does not result in the shutdown process being stopped.

Between the start and end of an application, there are two other messages related to the operational flow of your application. If the user brings another stack to the front, switches applications or otherwise causes the current stack window to become inactive, Revolution sends the suspendStack message. If the user action makes the application no longer the active application, Revolution issues a suspend message. The difference is that suspendStack is sometimes sent when your application is still active but the user has moved another of your windows in front of it, for example. The suspend message always means the user has suspended all Revolution activity and some other program is now in charge.

Neither of these messages is the actual trigger of the deactivation process, so you can’t interrupt the process by intercepting these messages.

The suspendStack message has a counterpart called resumeStack that gets sent when a stack that’s been suspended is reactivated. Similarly, Revolution issues a resume message when a stack that has been suspended is reactivated.

In Unix and OS X only, there is a final message related to startup and shutdown processing. The signal message is sent to the current card if another process sends your process a kill message. You can handle this message to do some cleanup before your process dies, but you can’t keep the process alive. (Technical aside: only kill signals 1 and 2 trigger the signal message; other kill parameters do not initiate a signal message.)

Handling Messages Related to Environmental Changes There are five system messages that are related to changes being made in the application environment. These are:

• newTool

Page 152: Software at Speed of Sound -- Dan Shafer

127

• editScript

• saveStackRequest

• nameChanged

• selectedObjectChanged

Whether or not the user of your application can cause any of these messages through their interactions with your stacks is in large measure under your control. You can set things up so these activities are possible or not.

The names of these messages are pretty self-explanatory. The newTool message is sent when the user changes tools. The editScript message is sent when the user attempts to open the script of an object. When the user selects a menu option that saves the current stack, the saveStackRequest message gets generated. If the user or a script changes the name of any object in the application, Revolution sends that object the nameChanged message. If the user selects a particular object in your application, Revolution broadcasts the selectedObjectChanged message to the newly selected object.

Handling Player Interaction Messages User interaction with player objects can generate three unique system messages:

• playPaused

• playStopped

• currentTimeChanged

Player objects also respond to all of the control messages discussed earlier in this chapter, of course.

The first two messages are self-evident in their functions. The currentTimeChanged message is sent by Revolution to a player when the user chooses a new scene in the player’s stream.

When you work with QuickTime Virtual Reality (QTVR) players, there are two additional messages you need to be aware of.

The nodeChanged message is sent to the QTVR player when the user switches to a new scene in the VR. The hotSpotClicked message gets sent to the QTVR player when the user clicks on an area of the QTVR player that has been defined as a hotspot. This is a QTVR’s means of interacting with the user.

Page 153: Software at Speed of Sound -- Dan Shafer

128

Revolution Messages I group together eight system messages that are primarily global in their effect or which operate between active stacks rather than within one stack. Three of these relate to stack usage, three are isolated-use messages, and two relate to error handling.

Handling Stack-Usage Messages The global messages grouped together in this discussion are:

• libraryStack

• releaseStack

• reloadStack

The libraryStack message is sent to a stack when it is added to the message path with a start using command. The releaseStack message is sent to a stack when it is removed from the message path with a stop using command.

The reloadStack message is rarely encountered but potentially useful. It is sent to the current stack if the user tries to open another stack of the same name (or, obviously, attempts to open an already-open stack).

The idle Message We’ve seen the idle message before. This message is sent whenever nothing is happening in Revolution; its timing is determined by two properties: idleRate and idleTicks . (This message has been deprecated in Revolution 2.x, which means you should not use it.)

The mainStackChanged Message The mainStackChanged message is one you’ll rarely encounter. It is managed by Revolution and, with very few if any exceptions, you won’t have to concern yourself with it. It is sent to the current stack if its mainStack is changed. Revolution uses this message extensively in the IDE, so if you do intercept and handle it, be sure to pass it.

The Error-Related Messages There are two potentially useful system messages that relate to error conditions.

The scriptParsingError message is sent to an object when its script can’t be compiled. You can intercept this message at the top level of your application and create a custom message for such occurrences. Doing so is probably not a bad idea in a commercial application intended for wide deployment.

Page 154: Software at Speed of Sound -- Dan Shafer

129

The errorDialog message is a potentially highly useful tool. It is sent to an object whenever one of its handlers can’t be run due to an execution error. The message is accompanied by three lines of detailed information that allow you to create highly customized error-handling and reporting routines.

Page 155: Software at Speed of Sound -- Dan Shafer

130

Chapter 7: Mouse and Keyboard Scripting In this chapter, you'll learn how to

• call and use several functions that help your scripts know where the mouse is and what it is doing

• simulate in a script the user clicking the mouse

• determine the status of the Command, Option, and Shift keys

• examine the keys being pressed by the user

Mouse Management Overview Transcript includes functions that enable us to keep track of where the mouse is, whether it’s been clicked, what is under it at the moment (or when it is clicked), and other useful information. In this section, I’ll discuss all the mouse-monitoring functions in Transcript.

Before I dive into the functions and techniques, I’d like to expose and explore a philosophical difference that has arisen among users and proponents of scripting languages like Transcript with respect to which of two fundamentally different approaches to take to the task of monitoring the user’s interactions with the system.

NOTE: For a more detailed discussion of this topic, see Jacque Gay’s excellent essay, “Polling the Mouse in MetaCard and Revolution” at http://www.hyperactivesw.com/polling.html. I have derived some of my observations below from that work.

If I am writing a script that involves moving an object on a card in response to the user dragging the object from one location to another, for example, I can approach the problem in one of two ways.

The first way is called “polling.” Transcript includes a number of simple commands that result in the system keeping constant track of the location of the mouse, for example. I can put one of those commands inside a repeat loop (see Chapter 8) and just keep an eye on where the user moves the mouse, then relocate the button to that point.

The second way involves the use of more discrete functions and methods which do not rely on constant polling by the system but rather on reaction to specific events that take place as the user interacts with the systsem.

Polling has one major advantage over the event-response approach: it is much easier to script. Particularly for people with no formal programmer training, it is much simpler. However, polling has one huge disadvantage: it is a blocking operation. This means that as long as the system is being asked to continuously poll the location of the mouse, for

Andrew
Highlight
Andrew
Highlight
Page 156: Software at Speed of Sound -- Dan Shafer

131

example, nothing else can be happening either inside the Revolution application or anywhere else on the user’s system.

For that reason, many people, including Scott Raney, the developer of the MetaCard engine that is at the heart of Revolution (as well as being a product in its own right, now owned by Runtime Revolution), discourage the use of polling. This results in the deprecation of several Transcript operations.

To describe how much more programming effort and knowledge are required to avoid using polling, you can check out the essay I mentioned earlier by Jacque Gay. A fairlyl simple three-line repeat loop for dragging an object around the screen ultimately turns into a four-handler, 20-line script. Granted, Gay adds a bit of polish to the new routine, but the fact is, avoiding polling, while a good idea in general, may make the machine more efficient, but only at the cost of reduced programmer efficiency.

Monitoring the Mouse In the last chapter, we learned about several system messages that report on the status of the mouse and its location in relation to objects. Because of the overriding importance of the mouse in any application, Revolution includes a number of other mouse-monitoring routines.

An important mouse-monitoring message is the mouseMove message. This message is sent whenever the user moves the mouse. This message enables you to keep track of where the user has positioned the pointer. It always sends two parameters. The first is the current horizontal position of the mouse pointer and the second is the current vertical position of the mouse pointer. With the recommendation that you no longer use mouseStillDown , mouseWithin , and idle in your scripts, the mouseMove message gives you a way of carrying out those operations without calling those deprecated functions. I’ll demonstrate that alternate, non-polling approach, shortly.

Three of these functions — the mouseLoc , the mouseH, and the mouseV — report on the screen coordinate location of the mouse at the time the function is called. Eight others, listed below, describe the location of the mouse with respect to objects, your stack, and the contents of fields in your stack window. These eight are:

• the mouseChar

• the mouseCharChunk

• the mouseChunk

• the mouseColor

• the mouseControl

• the mouseLine

• the mouseStack

• the mouseText

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 157: Software at Speed of Sound -- Dan Shafer

132

Nine of these 11 mouse-location functions also have equivalent functions that can be invoked with a “click” prefix:

• the clickChar

• the clickCharChunk

• the clickChunk

• the clickH

• the clickLine

• the clickLoc

• the clickStack

• the clickText

• the clickV

These functions tell you what is under the mouse pointer at the time the function gets called. They can be immensely useful in Revolution applications, particularly those involving the use of substantial amounts of text stored in Revolution fields. I’ll have much more to say about this group of functions later in this chapter.

(The word the in each of these names is essential. Without it, Transcript does not recognize the function. There is an alternative function syntax in which the word the is omitted and the function name is followed by a set of parentheses, such as mouseLoc(), but I find this a bit too “programmer-like” for my taste as a sort of hobbyist coder, so I tend to use the the form pretty consistently.)

NOTE: It is useful for you to understand the difference between a message and a function. A message is sent through your Revolution stacks and ultimately either intercepted and responded to by a script’s handler or it is simply ignored. A function is a part of Revolution that is executed within any handler that includes it in its code. In other words, you don’t write handlers for functions, you use functions within handlers.

Has the Mouse Button Been Clicked?

Let’s begin our discussion of these mouse-related functions by examining two — the mouse and the mouseClick — that determine the status of the mouse button at the time they are encountered in your scripts.

You use the mouse function to determine if the mouse button is being pressed at the moment it’s called. (This is a polling-based operation.) You can supply a parameter with the function that tells Revolution which mouse button interests you. The syntax for that particular variation is the mouse of button N, where N can be 1, 2, or 3. If you don’t include the “of button N” part of the function, button 1 is assumed. Button 1 is the left button on two- and three-button mice and the only button on the Macintosh’s one-

Page 158: Software at Speed of Sound -- Dan Shafer

133

button rodent. A value of 2 means the middle button on three-button mice. A value of 3 is the right button on two- and three-button mice and a Command-click on a Macintosh. This function returns “up” or “down” to reflect the state of the appropriate button at the moment the function is called.

The mouseClick function, on the other hand, looks only at button 1 of the mouse. If it has been clicked since the last time it was checked or since the event that triggered the currently running handler to be executed. If so, it returns “true”; if not, it returns “false.” (The mouseClick function is also a polling function.)

One common use of the mouseClick is to pause a script while you wait for the user to read or think about something, and then pause when the user clicks the mouse anywhere in your application (or, for that matter, outside it).

Such a handler might look something like this:

on myFunction open stack “infoWindow” wait until the mouseClick close stack “infoWindow” end myFunction

The stack “infoWindow” would tell the user to click anywhere to return to the original program.

How would I handle the same task without resorting to deprecated blocking functions like the mouseClick ? The likelihood is I wouldn’t even need a custom handler called myFunction to do this. I could use standard event-management techniques. In the original stack, I would have a button that would open the infoWindow stack that would look like this:

on mouseUp open stack “infoWindow” pass mouseUp end mouseUp

Then in the stack script of the stack called infoWindow, I’d have a second mouseUp handler:

on mouseUp close this stack end mouseUp

In this case, at least, avoiding the polling mechanism that blocks all other messages isn’t onerous and is probably a good design decision.

Page 159: Software at Speed of Sound -- Dan Shafer

134

If the user double-clicks the mouse, recall from Chapter 6 that the system sends the mouseDoubleDown and mouseDoubleUp messages which you can intercept in handlers to react to the user’s action. When the user does double-click the mouse, all the other mouse functions in this chapter are available under appropriate conditions.

Where is the Mouse?

When we want to know the exact location of the mouse pointer, we use one of the mouse-location functions. These fall into two groups: coordinate location finders and object-relative location finders.

The three functions that return the mouse’s location in terms of x-y coordinate positioning are:

• the mouseH

• the mouseV

• the mouseLoc

The last one -- the mouseLoc -- returns both the mouse’s horizontal location and its vertical location, while the mouseH returns only the horizontal location and the mouseV returns only its vertical position. The latter two return a single numerical value that tells how many pixels from the left edge or top edge of the current stack’s window the mouse is located. A positive horizontal value returned by the mouseH indicates the cursor is inside or to the right and outside the stack window. A negative value means it is to the left of the window and outside its boundaries. Similarly, a positive vertical value returned by the mouseV means the pointer is inside or below and outside the stack window while a negative value means it is above and outside the window boundaries.

Screen coordinates Before you can use the three mouse-location functions we are discussing, you must understand how Revolution views and addresses locations on the screen. We could present this information with a few paragraphs and diagrams. But because Transcript lends itself so well to exploration, we're going to set up a script in our laboratory to explore the screen, then summarize what we learn.

Laboratory: Finding Mouse Locations You can use an existing Laboratory stack card for this experiment or create a new one with nothing on it. Once you have opened the card you want to use, follow these steps.

(Note that in the following discussion we assume you are working with the original nine-inch Computer screen size. The bottom-right corner of the screen on a larger

Page 160: Software at Speed of Sound -- Dan Shafer

135

monitor will, of course, have a different set of values associated with it than those shown in the text.)

1. Open the card's script in one of the usual ways.

2, Type the following script into the script-editing window and click on the OK button when you're done:

on openCard repeat until the mouseClick put the mouseLoc end repeat end openCard

(Don't worry if you have no idea what the strange group of lines that begin and end with the word repeat are all about. We'll explain this programming technique in Chapter 8.)

3. Return to Browse mode.

4. Go to another card in the stack and then return to this one (or close the stack and re-open it) so that the card is opened and the handler can take effect. As soon as you do, the Message box appears with two numbers separated by a comma (see Figure 7-1). Move the mouse around without clicking the button and watch the numbers in the Message box change. As you roll the mouse to the right, the first number gets larger until the pointer reaches the right edge of the screen. Similarly, as you roll the mouse down the screen, the second number increases. Roll the mouse outside the window to the left and observe the first number becoming negative. Similarly, roll the mouse outside the window to the top and watch the second number get negative.

Figure 7-1. Tracking the mouse in the Message box

5. Put the pointer in the upper-left corner of the stack window, at the left edge of the window and just touching the window’s menu bar or control area. You should be able to get the two numbers to reach 0,0. If you move the pointer to the upper left corner of the window, you’ll see that you are at the location 0,-20.

6. Click the mouse. Notice that as you roll the mouse around now, the numbers don't change. That's because you've exited the openCard handler with the mouseClick , so the mouse position is no longer being tracked and reported.

7. Reopen the card and experiment with moving the mouse outside the boundaries of your stack window. Note the values that appear when you position the mouse in the upper left corner and in the lower right corner of the screen. Zoom your stack window so it is full-screen and position the cursor in the upper left and lower right corners again. Note the differences. The value of the mouse location when the cursor is in the lower right corner of your screen is a function of the screen resolution you’ve set for your

Page 161: Software at Speed of Sound -- Dan Shafer

136

computer’s display. For example, if you have it set to 1024 x 768 pixels, then the lower-right corner is going to have a location value very close to those numbers.

Each screen location, called a pixel (shorthand for picture element), has a unique address. You can see how finely a Transcript script can examine and manage screen locations. There are more than 750,000 individually addressable screen elements on a 1024x768 resolution screen.

How would we approach this same requirement without using the mouseClick so we can avoid polling? It turns out to be pretty straight-forward. Just create a handler in the stack or card script that intercepts the mouseMove event:

on mouseMove put the mouseLoc end mouseMove

This is cool, but it doesn’t really have the same functionality as our blocking approach because there’s no way to stop the handler from displaying the mouseLoc . There are a number of ways to accomplish this but perhaps the easiest to understand is to use a global variable and two handlers, one to deal with the mouse movement and one to stop the tracking process. The two handlers might look something llike this:

on mouseMove global gTrackLocation if gTrackLocation is true then put the mouseLoc end if end mouseMove on mouseUp global gTrackLocation put false into gTrackLocation end mouseUp

Now the mouseMove handler will keep displaying the mouseLoc as long as the global variable gTrackLocation is true. But as soon as the user clicks the mouse, the value of gTrackLocation is changed to false, and the next time the mouse moves, the location is no longer reported.

As you can see, working around the polling events and commands is almost always at least a bit more work, but it is clearly better program design in the long run. Modern-day operating systems are built around the notion of allowing multiple activities to appear to be happening simultaneously. If you write applications for those systems which block other activities, you will risk developing a reputation as someone who doesn’t “get it” when it comes to modern software design. On the other hand, if you’re writing these applications solely for yourself and you don’t really care what anyone

Page 162: Software at Speed of Sound -- Dan Shafer

137

thinks of your programming prowess, there’s no real reason not to take the shorter, less machine-efficient route and save yourself some design and coding time.

One coordinate at a time Using the same technique we just explored in the Laboratory stack, you can find out that the function the mouseH returns only the horizontal location of the mouse and the mouseV returns only its vertical position. Just change the mouseLoc in the script to one of these functions and observe the results.

Ending repeated commands You probably noticed in the openCard handler in the last Laboratory experiment that we used a repeat until the mouseClick statement. We'll be discussing such groups of statements in Chapter 8. For the moment, you should note that the mouseClick is one of the most useful Transcript functions for terminating repeating activities because it monitors users and reacts when they click the mouse anywhere within Revolution's boundaries.

What is Under the Mouse? It is often useful to know, when the user places the pointer over an object in your stack or clicks at a location on your stack’s window, what object or content lies directly beneath the cursor. You can find out:

• the text under the cursor

• the color of the pixel directly under the cursor

• the control over which the cursor is positioned

• the line of a scrolling field that is under the cursor

• the stack within which the cursor is positioned

What Text is the Mouse Positioned Over?

When the mouse is positioned over or clicked in a field containing text, you can determine the single character under the mouse, the chunking expression that defines that specific character, the chunking expression that defines either the word or the group of text containing the character, the actual word or group of text over which the mouse is positioned, and the line of a list or scrolling list field under the cursor.

The mouseChar returns the one-character string of the character over which the mouse is positioned. The mouseCharChunk function returns the chunking expression for that character. For example, if the mouse is positioned over the letter “e” in the word “Revolution,” and “Revolution” happens to be the first word in the field (or other object) containing the text, the mouseChar would return “e” and the mouseCharChunk

Page 163: Software at Speed of Sound -- Dan Shafer

138

would return “char 2 to 2 of field ‘Languages’” (or whatever the name of the field happens to be).

In that same situation, the mouseChunk would return “char 1 to 10 of field ‘Languages” while the mouseText would return the string “Revolution,” the word over which the cursor was positioned.

In a list field, we deal not with words but with lines of text. In such a field, the mouseChar and the mouseCharChunk both work the same as in a non-list field. But the mouseChunk returns a value that provides the beginning and ending character locations of the line of text in the context of the whole field. Thus if the first line of the field has 24 characters, and the user positions the cursor on the second line, the mouseChunk would return something like “char 26 to 84 of field ‘Languages’”. (Why 26 and not 25? There is an invisible carriage return at the end of the line, which is the 25th character in the field. The first character of the second line becomes the 26th in the field.)

Also, in a list field, the mouseLine returns a value such as “line 2 of field ‘Languages’” while the mouseText returns all of the text on that line as a string.

Page 164: Software at Speed of Sound -- Dan Shafer

139

What Object is the Mouse Positioned Over?

There are three kinds of objects over which a mouse’s position or click can be detected. Well, one of them isn’t really an “object” as such but it fits better here than anywhere else, so for now it’s an object!

You can find the color of the screen pixel over which the user is positioned or on which s/he has clicked by using the mouseColor . This function returns a comma-separated list of three numbers representing the RGB (red-green-blue) description of the color. (Consult a good HTML reference or go to HTML Center online at http://www.htmlcenter.com/tutorials/tutorials.cfm/89/General/ if you don’t understand RGB color codes.)

A string containing the name of the stack in which the pointer is positioned is returned by the mouseStack . If you want to know what control the mouse is positioned over or clicked in by the user, use the mouseControl function. This function returns a string consisting of the word “control” followed by the number of the control involved, as in “control 6”. (If you want to know the name of that control rather than its number, you can simply code an additional function into the statement:

put the name of the mouseControl into whatWasClicke d

Mouse Movement and Control Boundaries Recall that in Chapter 6 we examined the use of mouseEnter , mouseLeave , and mouseWithin messages to determine when the mouse moved within, remained within, or left the boundaries of a control.

We don’t need to say anything more about them here other than to remind ourselves that we can examine the mouse’s condition vis a vis a control by intercepting these messages and decoding their contents as shown in Chapter 6.

Clicking the Mouse for the User Sometimes in Transcript scripts you want to put the pointer in a particular location for some field entry work. Or you may need to simulate mouse clicking that you would normally expect from the user in a demonstration script where you want the user simply to watch what's going on. For these and similar situations, Transcript includes the click command. Its syntax looks like this:

click at location [with modifier key]

The location must be an explicit screen address, given as a pair of coordinates like those we have just finished learning about or an expression or variable that evaluates to such an address. If you want to simulate a mouse click as if the Shift, Command, or Option

Page 165: Software at Speed of Sound -- Dan Shafer

140

key were being held down, you can add the key name and the with connector. Here is an example of an Option-click command: click at 100,235 with optionKey

Modifier key additions are particularly useful when you are using the click command while managing one of Revolution's paint tools.

As with other Transcript commands we've looked at, click is most useful when viewed with a Revolution object. Every object has a screen location associated with it. You can find this address with the object’s location property, which may be abbreviated loc . This property contains the screen coordinates of the center point of the object referred to.

If you have a screen button called Button One, you can activate it exactly as if the user had clicked on it by writing a line of Transcript code like this:

click at loc of button "Button One"

Of course, you can also produce this effect with the send command:

send mouseUp to button "Button One"

There are a number of ways to accomplish many Transcript tasks. This is not the last time we will see such alternatives.

(Actually, click and send mouseUp differ in one important way. When you use the click command, Transcript generates both a mouseDown and a mouseUp message, exactly as when the user clicks the button physically. Sending the mouseUp message, on the other hand, generates only that message and no mouseDown. This distinction is probably safe to ignore most of the time but if you are dealing with Transcript activity at a message-passing level, it could become important.)

Revolution includes three functions to help you determine where the mouse was clicked by the user (or, for that matter, by your script). The clickLoc returns the location of the click as a point (two numbers separated by a comma). If you want just the vertical position of the mouse click, you can use the clickV and, if you want only the horizontal position of the click, use the clickH .

What's with the mouse? The last of this group of functions is simply called the mouse. It returns the value down if the mouse button is being pressed when the function is called, up if it is not. In some ways, its functions duplicate those of the mouseStillDown system message we discussed in Chapter 6.

Page 166: Software at Speed of Sound -- Dan Shafer

141

A key difference is that mouseStillDown is a message that can form the argument to the on portion of a handler, but the mouse is a function that returns a value and is used inside a handler. In other words, the mouse gives us a way to perform some steps inside a handler while the mouse is down. Put still another, more technical way, mouseStillDown is a message but the mouse is a function.

Laboratory: Is user holding down button? Let's do a little experimenting, without being concerned about what all the language elements mean or do. You can use any existing card with a button whose script you don't need or you can create a new card for this experiment. Then follow these steps:

1. Open the button's script in one of the usual ways.

2. Type in the following script and press the Apply button when you're done:

on mouseDown repeat while the mouse is down put empty wait 20 put "Working..." wait 20 end repeat beep end mouseDown

3. Return to Browse mode. 4. Click on the button and hold the mouse button down. Notice that the Message

box slowly flashes the message "Working..." as long as you hold down the mouse button.

5. Release the mouse button and note that the system beeps.

Obviously, you wouldn't write such a handler this way just to beep the speaker. But if you had more complex processing to take care of that you didn't want to carry out while the user was pressing the mouse button, this approach would work fine.

Keyboard Control and Monitoring

Transcript includes a few functions that deal with keyboard interaction. These functions allow you to detect whether a given key is up or down. You can detect the state of five special keys with these functions:

• alt (also known as the meta or Option) key

• Command (also known as Control) key

• Shift key

Page 167: Software at Speed of Sound -- Dan Shafer

142

• Caps Lock key

I’ve only listed four keys here because the commandKey (alternatively, cmdKey) function is exactly equivalent to controlKey (alternatively, ctrlKey ) when we’re dealing with any non-Macintosh system. On the Macintosh, both the Control and the Command key can be checked because both exist on the Mac keyboard.

Is That Key Down? Having seen that we can use the Command, Option, Control, and Shift keys with the click command, it will come as no surprise that we can also check the status of these keys to find out if they are up or down at the time the script checks them. Like mouse, the functions that monitor the keys return up if the key is not being pressed, down if it is. You can use these values in testing and branching operations in Transcript.

In fact, you can actually check not only on these keys but, using system messages discussed in Chapter 6, also on the enterKey , the returnKey , the controlKey , and the functionKeys . The same up and down logic that applies to the other keys we've been discussing also applies to these keys, with the exception of the functionKey message. When a function key is pressed, the message sent in Revolution includes the number of the key. The resulting handler portion looks like this:

on functionKey keyNo if keyNo = 5 then -- do some processing end if -- etc. end functionKey

Don't use function keys 1-4 for Revolution activities because most Mac keyboards pre-program and reserve these keys for editing operations.

Don't confuse the process of checking whether the commandKey is down using this approach with the use of the commandKeyDown message we discussed in Chapter 6. You can use the commandKey function inside a handler that processes some other system message. The commandKeyDown system message, on the other hand, must be intercepted in a handler named after the message (i.e., on commandKeyDown).

Laboratory: Keypresses Modify Results For this experiment, you can use an existing button whose script you no longer need, or you can create a new button on either a new or existing Laboratory stack card. After you have prepared the button, follow these steps:

1. Open the button's script-editing window using one of the usual methods.

2. Type the following script into the editor:

Page 168: Software at Speed of Sound -- Dan Shafer

143

on mouseDown put "Mouse is down " repeat while the mouse is down if the optionKey is down then put "with the Option Key" after Message exit repeat end if if the commandKey is down then put "with the Command Key" after Message exit repeat end if if the shiftKey is down then put "with the Shift Key" after Message exit repeat end if end repeat end mouseDown

3. Press the mouse button in the button you are using. The message "Mouse is

down" immediately appears in the Message box. Now press the Shift, Command, or Option key. Notice that the Message box adds information after its previous contents about which key you pressed. Confirm that the handler has been executed by continuing to hold down the mouse button and trying the other keys.

4. Release the mouse button. Now repeat the instructions in step 3 for all the special keys. Try holding down two of the special keys simultaneously. Notice that only one of them is acknowledged.

Even though this handler is longer than ones we've dealt with before, it is not very mysterious. The main body of instructions executes as long as the mouse is held down or until one of the special keys is pressed. When one of the keys we are monitoring is pressed, the handler adds some words to the end of the current contents of the Message box indicating which key was pressed. Then it leaves the loop and the handler ends. (These “if” and “repeat” constructs are the subject of the next chapter, so if they don’t quite make sense to you at the moment, don’t worry about it.)

Checking for two-key combinations You can link two key conditions together with the and logical operator and check for key combinations. We discuss the and operator in detail in Chapter 8. To look for the Shift-Command key combination, for example, you could perform a check like this:

if the shiftKey is down and the commandKey is down

Saving the key's condition Given the rapidity with which Revolution applications execute and the speed with which many people type, it is sometimes useful to store the state of a key and then check later to see if it was pressed when the user made the last selection or took the last action. This is particularly helpful when you write handlers that permit a single button to have

Page 169: Software at Speed of Sound -- Dan Shafer

144

more than one effect depending on whether a special key was held down while the button was activated.

Because these operations are functions that return a result, we can put that result into a container. Here is the skeleton for a mouseUp message handler that reacts differently to the message depending on whether the Shift, Option, or Command key is held down while the button is activated:

on mouseUp put the optionKey into optionStatus put the commandKey into commandStatus put the shiftKey into shiftStatus -- some other actions that don't depend on -- the keys' status take place here; these -- might be commands that are common to all -- variations of the button's theme if optionStatus is down then -- carry out the optionKey version end if if commandStatus is down then -- carry out the commandKey version end if if shiftStatus is down then -- carry out the shiftKey version end if end mouseUp

Watching User Keystrokes There are times in constructing a Revolution application when you would like to be able to watch what the user types at the keyboard and react instantly to an unexpected or incorrect keystroke. Beginning with Runtime Revolution 2.0, that became possible with the addition of the keyDown system message. This message was discussed in Chapter 6 in the context of describing all of Runtime Revolution's system messages. Now let's take a closer look at how you could make practical use of this message in dealing with keyboard I/O.

Assume that you'd like to be sure that the user types only numeric values into a field you've created on a card. By intercepting the keystrokes as the user types them and rejecting any keystroke that doesn't meet the criteria you set up in the keyDown handler, you can achieve this result. Here is a handler that will screen out all but numbers and related punctuation from a field:

on keyDown theKey if theKey is not a number then beep else pass keyDown end keyDown

This handler is not completely usable as it is because it should also watch for other keystrokes that it may or may not want to reject. For example, you may want the user to be able to tab from this field, possibly use the Return key to mean he or she is done

Page 170: Software at Speed of Sound -- Dan Shafer

145

entering data, press the Backspace key to delete an entry, and maybe use other keys as well. Expanding the handler to deal with those needs would not be difficult and you might enjoy trying that as an exercise. As written, this handler will only reject alpha characters; special keystrokes like those listed here will be accepted and acted on.

You would also want, in a real-world application of this type, to be sure the field was empty before all of the new data was entered into it (or otherwise set up for data entry). This could be handled easily in an openCard or openField handler, for example.

Page 171: Software at Speed of Sound -- Dan Shafer

146

Chapter 8: Control Structures and Logical Operators In this chapter, you will learn

• how control structures are used in Transcript scripts to execute groups of instructions repeatedly or conditionally

• what logical operators and related functions are available in Transcript

• how to use special Transcript commands to gain better control over loops

Loops and Conditions: Background If you have programming experience in a traditional programming language, feel free to skip this discussion and move to the next section, "If-Then Processing." But if Transcript is your first programming language or you're a bit rusty in the fundamentals, reading this section will make the rest of this chapter more understandable.

Most computer programs — and Transcript handlers are no exception — execute linearly, starting with the first instruction, then executing the second, then the third, and so on until they come to some kind of logical end. Along the way, procedural programming languages often include branching instructions that send the program to some other part of the code semipermanently (as with the BASIC GOTO statement) or temporarily (for example, GOSUB in BASIC). These languages also generally include instructions to execute one or more commands conditionally (that is, only if a certain condition is met) or repeatedly (that is, until some condition arises, as long as some condition is true, or a specific number of times).

In all cases, these constructs give programmers great flexibility in the way their programs manage data and interact with the user. It can be argued—and often is — that no interesting or useful programs can be written without such conditional processing and looping constructs. Avoiding that philosophical issue, it is clear that meaningful programs are often more difficult and time-consuming to design and write without such constructs.

If-Then Processing The first control structure we will discuss is the if-then-else construct, which in one form or another is part of most scripting and programming languages.

Many of the decisions we make in our lives can be expressed as if-then decisions. If it is Sunday, then I won't get up early and go to work. If it is 95 degrees outside and the sky is azure blue, then I won't take a raincoat with me when I leave this morning. In each case, notice that we have the conditional "flag" in the word if, a word a wag once declared to be the biggest little word in the English language. This word is followed by a description of a state of affairs or sequence of events that is either true or false. If it is true (that is, if it really is Sunday), the second part of the statement is carried out (that is, I don't get up early to go to work).

Page 172: Software at Speed of Sound -- Dan Shafer

147

If-then-else decisions in Transcript are similar to such decisions in our daily lives.

General format and use To undertake some conditional processing in Transcript, you have a choice of two related approaches. The first has one set of conditionally executed commands that are carried out only if some condition is true. Its general form looks like this:

if <condition> then <series of commands> end if

This format can be abbreviated if there is only one command to be executed. In that event, the end if statement is not required. The conditionally executed command can be on the same line as the if-then:

if <condition> then <command>

The second approach to conditional program processing provides two sets of alternative commands: One is carried out if the condition is true and the other is carried out if it is false. This approach is known as the if-then-else approach. The formatting of this approach is identical to the first one above. The general format looks like this:

if <condition> then <series of commands> else <alternate series of cormands> end if

The simplest form of the if-then-else construct is:

if <condition> then <command_l> else <command_2>

Formatting, which is generally not an issue in Transcript programming, becomes important in dealing with the if command and its variations. The issue centers around when you must supply the end if statement. Many Transcript programmers have had difficulty sorting out this question. If you run into situations where Transcript's script editor refuses to indent your if-then-else loops correctly and you can't figure out why, ask yourself these questions:

1. How many else clauses are there? (Note that if you put the key word else on a separate line from the command to be executed with it, Transcript sees this as two clauses, not one.) If you have only one line in the else clause, you should not include the end if statement. In fact, it is wrong to do so. If you have two or more lines in the else clause, the end if is required. If you have no else clause, then go to the next question.

Page 173: Software at Speed of Sound -- Dan Shafer

148

2. How many then clauses are there? (Again, Transcript sees the word then on a line by itself as a then clause line.) If you have only one line in the then clause, you should not include the end if statement. Otherwise, it is required.

You must, of course, separate clauses into lines; you cannot combine commands in one Transcript line inside an if statement any more than you can anywhere else in Transcript.

It is possible to set up multiple conditions in the if clause of an if-then or if-then-else construct. In the event you need such multiple conditions, you will find it necessary to connect them with the key words and or or . We'll see some examples of this usage later in the chapter.

Nesting if statements You can nest if-then and if-then-else statements. In other words, commands executed conditionally can themselves be a set of conditionally processed commands. This ability lends itself to powerful—but potentially complex— programming structures. Here is an example of a nested set of if-then-else statements:

on mouseUp if the optionKey is down then if the shiftKey is down then beep 5 else beep 3 end if else beep 1 end mouseUp

The button this script is attached to beeps once when it is clicked, unless the Option key is held down at the same time, in which case it beeps three times. If the Shift and Option keys are held down together when the button is pressed, Runtime Revolution beeps five times. Notice that the outermost if-then-else construct has only one statement in the else portion, so an end if is not required. But the inner if-then-else needs an end if because otherwise we’d have to else clauses adjacent to one another and the script interpreter would be unable to resolve the ambiguity.

Laboratory: Nested if statements

Let's go back to the Laboratory stack. Pick any card with a button whose script is no longer needed, or create your own. Then follow these instructions:

1. Open the Script Editor for the button.

2. Type the following script into the window and click Apply when you are done and the handler is syntactically correct:

Page 174: Software at Speed of Sound -- Dan Shafer

149

on mouseUp if the optionKey is down then if the shiftKey is down then beep 5 put "Shift-Option Combination" into Message else beep 3 put "Option Key Alone" into Message end if end if end mouseUp

3. Select the Browse tool.

4. Click on the button, first with the Option key held down, then with the Shift-Option keys held down, and finally with no keyboard keys held down. Notice not only the different beep combinations, but also the changing notices in the Message box.

This is the same basic handler script as the one we just looked at in much simpler form. It works the same, but this time we've added multiple statements after each else, requiring that each if clause be ended with an end if . Although this script is simplistic and doesn't do anything to write home about, it does demonstrate the flexibility of multiple nested if-then-else statements. A single handler responds to an event that can be sent in three ways: alone, with the Option key, or with the Shift-Option keys.

Avoiding Complex if Statements With switch

If you have a variable whose value can take on more than two values, and when the flow of your script will need to respond to this range of values, if-then statements can become complex and obtuse. In such situations, you should use the switch control structure rather than if-then-else .

The switch control structure has two different possible syntaxes. They are functionally equivalent and for the most part the decision which to use is a personal style consideration.

In the first form, the switch statement is followed by the name of a variable or a Transcript expression. In that event, the switch contains any number of case statements, one for each anticipated value that results from examining the variable or executing the expression. For example, let’s say we have a variable called userTitle that we expect will contain one of the following strings: President, Vice-President, Director, Manager, or Supervisor. Depending on the user’s title, we want to do something slightly different at some point in our program. The script fragment that would deal with this need would look structurally like this:

Page 175: Software at Speed of Sound -- Dan Shafer

150

switch userTitle case “President” -- process a President person break case “Vice-President” -- process a VP break case “Director” -- process a Director break case “Manager” -- process a Manager break case “Supervisor -- process a Supervisor break default -- process if userTitle has some other value end switch

You can see how much more efficient this is to write than the if-then-else equivalent:

if userTitle is “President” then -- process a President else if userTitle is “Vice-President” then -- process a Vice-President etc.

Notice at the end of the switch construct I’ve placed a default statement group. As each of the cases ends with a break statement, if we get all the way through the list and no case has been matched -- say, e.g., the userTitle was “Programmer” -- then the default statement group is executed if it exists. It is, however, entirely optional. You can just do nothing if one of the anticipated conditions isn’t met. An alternative approach you can use if your entire handler consists of a switch statement or if it is the last thing in the handler, is to replace break with an exit statement. You could use either exit to top if you just want to stop processing and wait for the next event or exit handlerName to stop executing the current handler and return control to the handler that called this one.

If the switch statement is followed by a Transcript expression, that expression is evaluated once and then its result compared to the argument associated with each case in the construct. For example, in a game routine you might generate a random number between 1 and 32 and wish to do something different for various number combinations. You could end up with code that looks like this:

switch (random(32)) case 1

Page 176: Software at Speed of Sound -- Dan Shafer

151

case 3 case 11 case 15 -- some statements to execute break case 2 case 4 case 6 case 32 -- some statements to execute break etc.

This example also demonstrates the use of multiple case statements with a single result. If the random number generated is 1, 3, 11, or 15, the first statement group will execute. The break will cause script execution to continue after the end of the switch construct. If the number doesn’t match any of the first case group, then it examines the values in the second, and so on.

The second \syntax for the switch statement involves a simple switch with no variable or expression following. In that syntax, each case statement will have a logical expression -- i.e., an expression that evaluates to true or false -- associated with it. You will find less frequent need for this form of the switch construct, but it does come in handy. For example, you might want to go through a stack of cards, look at the contents of a field, and look for one of several specific strings in that field. Here’s a small routine that would handle that kind of task:

go next switch case (field “taste” contains “delicious”) case (field “taste” contains “good”) -- some statements to execute break case (field “taste” contains “horrible”) case (field “taste” contains “awful”) case (field “taste” contains “disgusting”) -- some statements to execute break end switch

It’s important to know that if you omit the break at the end of a group of statements (even a group of one statement) to execute when a case condition is true, the processing continues with the next case group. In rare circumstances, that’s exactly what you want to do. Most of the time, though, you’ll want to end each case statement list with a break statement to send the script to the first line following the end switch statement.

Page 177: Software at Speed of Sound -- Dan Shafer

152

Conditional Operators and Calculations Now that we've seen how if-then and if-then-else combinations work, let’s see how we create their conditions.

True or false tests All conditions in conditional processing must ultimately lead to a true or false situation. Any question that can be answered "yes" or "no" can be used to formulate a condition for an if clause in Transcript.

You can set up tests for equality (if x = 3 then...) or inequality (if not x = 3 then...) but generally not for mathematical formulas (if x*4 then...) or other expressions that don't produce a true or false, yes or no, 1 or 0 result. (But note that we could use a formula as part of the equality test: (if x*4 = 0 then...). Functions and expressions that do return such results are referred to as logical operations or Boolean functions.

In Transcript you will frequently test many things in the condition portions of if statements.We can divide these tests more conveniently into determinations of:

• equality of two elements

• inclusion of one element in another

• status of a particular aspect of the system

• type of a particular container or value

• existence of a specific object

Let’s look at each kind of test.

Equality conditions The test condition you will undoubtedly use most often in Transcript scripts is the = operator. It has a synonym in the is command.

The following examples of equality conditions using is are similar to those you will frequently encounter in programming Transcript scripts:

if field "Name" is empty if the optionKey is down if x is 5 if it is "excited"

These are identical in effect to:

if field "Name" = empty if the optionKey = down if x = 5

Page 178: Software at Speed of Sound -- Dan Shafer

153

if it = "excited"

As you might expect from a language as English-like and flexible as Transcript, is has an opposite just as = does. The opposite of the equal sign is written as <> and the opposite of is , logically enough, is is not . In many cases, Transcript has values that make the use of is not and other inequality operators unnecessary. For example, you hardly need to write:

if the optionKey is not down

when the shorter and clearer

if the optionKey is up

is available. But in other cases, the inequality operator is exactly what's needed:

if field "Name" is not empty if x <> 5 if it is not "excited"

Comparison operators Sometimes, it is not enough to know that an item is not equal to another. You need to know if it is greater than or less than the other object. For these situations, Transcript includes the usual programming language complement of comparison operators. These operators are summarized in Table 8-1.

Table 8-1. Comparison operators

Operator Meaning Example > Greater than 15>53 returns false

>= Greater than or equal to 15>=53 returns false

33 >= 33 returns true

<= Less than or equal to 15<=53 returns true

33<=33 returns true

33<=15 returns false

Inclusion Besides equality and inequality, your Transcript scripts can test for the presence of a sequence of characters in a field or text container. You can use is in, contains , or is among. All three of these operators perform text matches that are not case sensitive. They look at one text string—the container—and see if another text string (or if

Page 179: Software at Speed of Sound -- Dan Shafer

154

any of several text strings) is located anywhere within it. The syntax for these three commands is as follows: if <source string> is in <container> if <container> contains <source string> if <source string> is among the {words | items | li nes} of <container>

As you can see, the only syntactical difference between the first two commands is that is in places the string to be searched for first, but contains puts it last.

The is among operator, by contrast, requires that you define the size of the chunk expression you want to use for a match within the container whose contents are being searched for the source string. This allows you to search for a string only among the words or items or lines of a container. For example, if you wanted to find the word “chocolate” only if it appeared on a line by itself in the target container, you could write a line like this:

if “chocolate” is among the lines of field “Flavors ”

Laboratory: Testing for inclusion

Use either an existing Laboratory stack card with a reusable field or create a new field on either an existing card or a new card. Name the field “foo”.

1. Click into the field. Type in the following text with capitals exactly as shown:

This is a dumb TesT

2. Now open the Message box if it isn't already open, or click in the Message box if it is open. Make sure the box is empty.

3. Type:

field “foo” contains "test"

and press Return. The Message box displays "True." Clearly, Transcript has found the string and ignored the case differences. Try the same thing with other combinations of uppercase and lowercase if you like.

4. Type:

"dumb " is in field “foo”

Be sure to include the space after the word dumb. Again, the Message box displays "True." You can use this approach to find any combination of characters or spaces in the text of a field.

5. Type:

Page 180: Software at Speed of Sound -- Dan Shafer

155

“dumb“ is among the words of field “foo”

This time we left out the space after “dumb”. The answer produced in the Message Box is “true.

6. Now let’s put the space back in and try the same command again:

“dumb “ is among the words of field “foo”

This time the Message Box produces a result of “false” because the word “dumb” followed by a space is not a word, so the string is not among the words in the field.

Status We have already seen in Chapter 7 how to test the state of the Shift, Option, Command, and Control keys and react accordingly. As we saw in Chapter 4, Revolution objects have properties associated with them. These properties enable us to find out basic information about the objects other than their contents. For example, using property management commands we can find out if a field is presently visible, an object's name, how many cards, buttons, fields, or groups are in a stack, and dozens of other useful pieces of information.

Testing and changing the condition of these properties is one of the most frequent kinds of conditional processing you'll do in Transcript. The general form for using these properties as conditions is straightforward:

if <property name> is <value>

To give you an idea of what kinds of things you might test for, here are some sample lines:

if field 1 is visible - if field "Test Field" is empty if the number of cards > 53 if lockScreen is true

Not all properties that can be attributed to Revolution objects are subject to if-then testing or could logically be used in conditional processing statements.

Type You can test a container's contents or an explicit value to determine if it is one of the following types of Revolution entities:

Page 181: Software at Speed of Sound -- Dan Shafer

156

• number

• integer

• point

• rectangle

• date

• logical expression (Boolean)

• color

This test is handled by means of an extension to the is command we discussed earlier in this chapter. For example, to determine whether a card field contains a valid date or not, you can use a test like this:

if fld “foo” is a date then -- do some processing

You can use either "a" or "an" as the article preceding the type of value for which you wish to test.

If you want to determine if a field contains a number or an integer, you must use the value function to extract the value of the field before testing its type. This is not necessary if you are testing a variable's type, however.

For the purposes of this operator, Transcript defines a rectangle as any four numbers separated by commas. While this makes it possible to determine whether a particular expression could define a point or rectangle, it doesn't really tell you if the expression is a valid rectangle.

Existence

You can test for the existence of certain types of entities in the system or the Revolution environment. This enables you to be sure that, for example, a particular field exists before sending it a message or trying to put some information into it.

Using the there is operator, you can test for the existence of:

• windows

• menus

• menu items

• files

• cards

• groups

• buttons

• fields

• stacks

Page 182: Software at Speed of Sound -- Dan Shafer

157

• files

• folders

• executing process

In all cases but windows, you simply supply an expression that identifies the object for which you wish to test. You must inquire about the existence of a specific object (e.g., field 1), not a generic object (e.g., a field). For example, to find out if there is a field 1 on the current card, you would use this statement:

if there is a field 1 then -- some processing

To test for a window, you must precede the expression with the key word window, as in:

if there is a window "Test Stack" then — some processing

You can supply path names to point to specific stacks and files that you want to confirm exist or don't exist.

The there is a operator has a negative counterpart, there is no . As you can no doubt determine for yourself, this operator tests for the non-existence of an object.

Using the constants true and false When you do conditional processing programming in Transcript, you can use the built-in constants true and false. These constants are not often used with most of the conditional processing situations we've discussed in this chapter because the true or false nature of the condition is apparent. A button is either up or down. Writing a line such as

if the optionKey is down is true

is superfluous.

But sometimes the built-in logical constants are handy. Perhaps the most obvious case is the process of what is called in programming, “setting flags.” In complex programs, we often want to keep track of a condition in the system. For example, we may want to know if the user has clicked on Button 1 because, if so, certain processing won't be necessary. So we set up a "flag" variable — call it pressed in this example — to keep track of that value. Then we can test it at the appropriate time. The framework for the example we just described is:

on someEvent set pressed to false

Page 183: Software at Speed of Sound -- Dan Shafer

158

-- some intermediate processing if pressed is false then -- some processing end if end someEvent

Logical connectors

One final topic should be covered to complete our discussion of conditional processing. Often, a combination of conditions must be tested before a script can proceed with processing. Staying with our by-now-familiar special key checking, for example, we might want to know only if any of the special keys has been pressed. We could get that information by programming a construct such as:

on mouseUp if the optionKey is down then put "Special key dow n" if the shiftKey is down then put "Special key down " if the commandKey is down then put "Special key do wn" end mouseUp

Pressing any combination of these special keys when the mouse button is clicked puts the message "Special key down" into the Message box. (We should note that if more than one special key is used, the message is actually placed in the Message box as many times as there are special keys pressed. But the effect is all but unnoticeable to the user, so it is not significant in this context.)

But this is more code than we need. If we want to display the message if any special key is being pressed, we can use the or connector. This connector links logical conditions so that if any one of the conditions connected by or is true, the condition succeeds. Thus, the previous handler can be reduced to this:

on mouseUp if the optionKey is down or the shiftKey is down o r \ the commandKey is down then put "Special key down" end mouseUp

Similarly, if we want to display a special message only if all the keys are down, we use the and connector. Conditions hooked together with and only succeed if all the conditions are true.

The third logical connector in Transcript is not . It is used to negate or reverse logic. It can be applied to any equality condition check to make it a check for inequality. It can also be combined with and and or to create complex criteria in control constructs.

Page 184: Software at Speed of Sound -- Dan Shafer

159

Looping Commands Besides conditionally executing instructions in a handler, we might want to execute some particular step or steps more than once. The repeated execution of commands in Transcript is accomplished with an instruction that is named, appropriately enough, repeat .

This instruction should properly be looked at as four separate instructions. The variations on the repeat theme are:

• repeat for

• repeat with

• repeat while

• repeat until

• repeat for each

Traditional programming languages include some or all of these constructs. Transcript, however, includes something most other programming languages lack: two ways to "escape" from an executing loop without finishing some or all of its instructions.

Basic looping concepts All repeat structures in Transcript work similarly. They all start with the word repeat and end with the phrase end repeat . They all execute the commands contained between those key words zero or more times. And they all end when the circumstances under which they are expected to execute are no longer valid, unless they are interrupted sooner by an exit repeat command.

Inside a loop, all the rules of program execution apply exactly as they do in a complete handler. Any conditional statement groups involving if-then or switch combinations are evaluated and executed as if they were the only statements in the group. Local and global variables known to the handler can be used anywhere inside the repeat loop.

Quite often, a handler consists entirely or nearly entirely of a single repeat loop. We saw one such loop in Chapter 7 when we performed a Laboratory exercise to track the coordinate position of the mouse pointer in a card. You may recall that the loop executed until you clicked the mouse.

The most crucial idea in writing loops in any programming language, including Transcript, is to make sure there is an escape route. There are two ways to create such routes in Transcript:

• by including an exit repeat condition that explicitly leaves the loop when some event occurs

Page 185: Software at Speed of Sound -- Dan Shafer

160

• by ensuring that the condition under which the repeat command is executed eventually changes to a condition under which it will stop

Failure to do one of these results in a dreaded outcome called an infinite loop, one of the most common mistakes in programming. (Note that in most cases if you script an infinite loop in the Revolution development environment, you can force it to stop by pressing Ctrl-period or Ctrl-break on Windows or Unix, or Command-period on Mac systems.)

Basic repeat conditions Whether we are talking about Transcript or a conventional programming language, there are two general categories of conditions under which loops can be programmed to execute:

• for a specific number of times

• as long as some condition remains true / In the first category of repeat loops, it is the reaching of some specific value in a variable or an upper limit on the number of iterations set as a condition. In the second category, the circumstance that causes the loop to stop is the changing of a condition.

Using object-counting in repeat conditions One of the most useful things you can find out in Revolution for use in repeat loops is the number of some kind of object with which you wish the loop to deal. For example, if you want to zip through a stack and look at each card individually, you want to know when you're done looking at all the cards. If you want to perform some operation or test on all the card buttons on a given card, you may want to know how many buttons you have to deal with. Although you can "hard-code" such information, this is practically never a good idea; any future changes to the stack design might not get picked up in the scripts affected by those designs. All manner of confusion can result.

You can have Revolution supply you with the number of buttons or fields on the current background or card, the number of groups in the stack, or the number of cards in the stack, among other things.

It then becomes easy to write loops such as this one:

repeat for the number of cards if field "Accepted" contains "Yes" then add 1 to total go to next card end if end repeat

Page 186: Software at Speed of Sound -- Dan Shafer

161

You will find frequent need for such repeat-loop counter controls that involve knowing the number of some particular kind of object with which the handler must be concerned.

The repeat for and Basic repeat commands The naked repeat and its slightly more verbose cousin, repeat for, are the most straightforward of the looping constructs in Transcript. They simply tell Revolution how many times to execute a loop. In the absence of an exit repeat command inside the loop, the instructions execute exactly the specified number of times, and then the handler proceeds with the rest of its processing.

Here is the syntax for the repeat for construct with, as you can see, two optional words that, when omitted, strip the command to a simple repeat <number> construct:

repeat [for] <number> [times] <statements> end repeat

The following opening lines of repeat loops are equivalent:

repeat for 11 times repeat for 11 repeat 11 times repeat 11

The number argument need not be an explicitly supplied numeric value. It can be a container that holds a number or an expression that evaluates to one. For example, you might ask users how many times they want some action to occur, store the answer in the variable numberOfTimes, and then set up a repeat loop like this:

repeat numberOfTimes times -- some statements to execute end repeat

You will find this approach to the repeat loop most useful when you are performing some process repeatedly but want to insert a wait command to slow things down a bit. This command causes a delay measured by the number of ticks, or 60ths of a second, supplied as an argument. You have undoubtedly noticed, for example, that when we use commands such as beep 3 in our scripts, the three beeps take place in such close proximity that they sound like only one beep. By putting the beep command inside a repeat loop, we can separate the sounds by some silence, achieving the desired effect:

repeat 3 times beep wait 4 end repeat

Page 187: Software at Speed of Sound -- Dan Shafer

162

Experiment with the value supplied as the time to wait to convey the audio message you want.

You will also frequently use the repeat for approach to looping when you are scanning through all the fields or cards to look for something or to change the data. For example, you might have a client-billing stack that includes on each card a field with the billing rate for that attorney. If the partners decide to increase billing rates by 10 percent, you could write a small repeat for loop like this:

repeat number of cards times put field "Rate" * 1.1 into field "Rate" go next card end repeat

A useful variation on the repeat for construct is the repeat for each approach. This construct allows you to repeat a set of statements for each chunk in a specified string or for each element in an array. (I’m going to defer the discussion of arrays until Chapter 10.)

The repeat with command The repeat with command adds another level of complexity to the condition. In the repeat with approach, you use a special variable, called a counter, that increases or decreases by one during each cycle through the loop until it reaches a predetermined value. Then the loop stops executing.

The syntax for the repeat with command looks like this:

repeat with <counter> = < start> [down]to <end> <commands> end repeat

If the value for start is 1, repeat with is the same as repeat for. In other words, this repeat loop

repeat with x = 1 to 10 <commands> end repeat

does the same thing as this one

repeat 10 times <commands> end repeat

There is an important exception to this equality. If you need to use the number of times through the loop as a value, only the repeat with approach will work. Returning to

Page 188: Software at Speed of Sound -- Dan Shafer

163

our time-billing example, suppose each attorney's card contained several billing rates and the fields were called (for purposes of simplicity) Field 1, Field 2, and so on. You could update all the fields on a single card with a single repeat loop like this:

repeat with x = 1 to number of fields put Field x * 1.1 into card Field x end repeat

But if billing rates start with Field 7 and are the last fields on the card, a simple modification handles that case as well:

repeat with x = 7 to number of fields put card Field x * 1.1 into card Field x end repeat

In this last example, it would be difficult, if not impossible, to use a basic repeat for loop to solve the problem.

The step option allows you to change the value of the repeat loop counter by an amount other than 1 each time through the loop. For example, if you want to skip every other line of a field, you could do something like this:

repeat with x = 1 to the number of lines of field 1 step 2 put line x of field 1 after field 2 end repeat

The repeat while construct You use the repeat while command to carry out a set of instructions as long as some condition is true. As soon as the condition is false, the loop stops executing. The syntax for the repeat while construct looks like this:

repeat while <condition> <commands> end repeat

The condition here is identical in use and form to those we discussed with if processing previously in this chapter. It must evaluate to a true or false value, and it can use any of Revolution's built-in functions to do so. If the condition is false the first time Revolution encounters the loop, the loop never executes.

Laboratory: Using the repeat while command

Open the Laboratory stack again. We need a card with a button and a field whose contents you can reuse. Find an old card with this combination of objects or create a new

Page 189: Software at Speed of Sound -- Dan Shafer

164

one. The field must be Field I, or you must make appropriate changes to the following script.

1. Open the button's script in one of the usual ways.

2. Type the following script into the window and click Apply when you're done, then close the window:

on mouseUp repeat while field 1 < 64 put field 1*2 into field 1 end repeat beep end mouseUp

3. Select the Browse tool.

4. Enter the number 2 into the field.

5. Press the button. A series of numbers appear in the window, until the value 64 appears. Then the loop stops, your system beeps, and the handler ends.

6. Put the number 75 (or any number higher than 64) into the window and notice what happens. The repeat loop is never executed. Instead, you get the expected beep sound and the handler ends. The first time the repeat loop was entered, the value of the field was greater than 64, so the loop was never carried out.

The repeat until command The last of the four regular repeat forms we will examine is the repeat until command. It is easy to understand because it is the flip side of the repeat while command we have just examined. The commands within its boundaries execute as long as the condition remains false. As soon as the condition turns true, the loop stops. This generally requires a reversal of the logic of the repeat while loop's condition. Here's the syntax for repeat until :

repeat until <condition> <commands> end repeat

To execute the example in our previous Laboratory exercise the same way using a repeat until loop, we have to reverse the logic of the condition. Instead of checking for the field's value to be greater than 64, we check for it being less than or equal to 64:

on mouseUp repeat until field 1 >= 64 put field 1*2 into field 1 end repeat beep end mouseUp

Page 190: Software at Speed of Sound -- Dan Shafer

165

If you want to experiment with this script, simply edit the repeat line of the script from our last Laboratory experiment and run the handler.

We use the condition "greater than or equal to 64" rather than "equal to 64" in the previous example for an important reason. If you use only an equal condition (that is. Field 1=64) and if Field 1's value never gets to exactly 64, the loop could become an infinite one. For example, if the user puts a 3 instead of a 2 into Field 1 at the start, doubling the value each time results in answers of 6,12,24,48,96,192, and so on. Because 64 never appears, the loop just keeps executing. In the Laboratory example, we were sure that the value 64 would eventually appear. But unless you can guarantee the ceiling value will be reached, use conditions that are met without an infinite loop.

The naked repeat command You will occasionally encounter one last form of the repeat command in scripts: the naked repeat. With no for, with, while, or until, the repeat command means repeat forever. (You can even add the word forever if you want to make it clearer.)

In 99 percent of all cases where you use the repeat command alone, you will supply a means of exiting the loop using the technique described later in this chapter. And you probably could design the loop as a more conventional repeat loop using one of the other ending conditions we've been studying.

But in some limited circumstances, you want to keep all or a major portion of your script's processing in an intentionally infinite loop. Such loops can give the programmer more control over the user's interaction with the program.

Control Within repeat Loops Transcript includes two powerful commands to change the course of action within repeat loops. One, next repeat , cuts short the execution of part of a collection of repeated statements while continuing the loop at the next iteration. The other, exit repeat , causes the loop to stop executing and the script to begin processing at the first statement after the end repeat line.

The next repeat command Although most programming languages do not have a command equivalent to the next repeat command, it has some handy uses. Within any repeat loop, you can use a command such as

if <condition> then [<conmands>] [end if] else next repeat [<commands>]

Page 191: Software at Speed of Sound -- Dan Shafer

166

to make the program go back to the top of the repeat loop, without executing any statements between the next repeat command and either another next repeat command or the end repeat command. If any commands appear between then and next repeat , the end if statement is also required, as you will recall from our previous discussion of if-then combinations.

Here is a generic example of the use of the next repeat command:

repeat with x = 10 to 50 <statementl> <statement2> if <condition2> then beep 3 next repeat end if <statement3> end repeat

This loop executes 41 times, once for each value of x from 10 to 50, inclusive, unless the condition specified in condition2 becomes true before that time. In that event, the system beeps three times and statement3 is not executed. But statementl and statement2 continue to execute until the loop ends normally. If condition2 is never changed, statement3 is not executed again because each time through the loop, the if statement catches the execution, finds the condition to be true, and returns to the top of the loop.

Again using our time-billing example, assume the partners want to not only increase all billing rates by 10 percent, but also make sure no billing rate is lower than $125. The following loop handles that task:

repeat with x = 1 to number of fields put card field x * 1.1 into field x if field x >= 125 then next repeat put 125 into field x end repeat

There are other ways to handle this problem, but this solution works, is efficient, and demonstrates the use of the next repeat command nicely.

Counting backwards with down to When you need to count down from a higher number to a lower one rather than in the more common ascending order, you can substitute down to for to in the repeat with loop structure:

repeat with x = 10 down to 1

Page 192: Software at Speed of Sound -- Dan Shafer

167

put x beep x end repeat

Defining a different increment As I have already mentioned, repeat loops in Transcript that use values with the with and for approaches do not allow you to increment or decrement the value of the counter by any value other than 1. Sometimes you want to increment a value by another value. In those cases, you can use the next repeat function with the mod operator to change the increment. (The mod operator finds the remainder in a division problem. For example, the result of 6 mod 3 is 0 because 6/3=2 with no remainder. But 39 mod 2 is 1 because 39/2=19 with a remainder of 1.)

Here, for example, is a loop that effectively increments the counter by 2 each time through the loop:

repeat with x = 1 to 100 if x mod 2<>0 then next repeat put x wait 1 second end repeat

You can confirm that this loop works by attaching it to a button in a Laboratory card. The numbers 2,4,6, and so on up to 100 are displayed in the Message box. Note that all the usual rules about using if-then constructs, described previously in this chapter, apply to next repeat commands.

The exit repeat command The exit repeat command is straightforward. It is almost always used in an if-then construct. The exit repeat command causes the loop to terminate immediately. The syntax for this command is:

if <condition> then exit repeat

As with other if-then constructions, this command can incorporate other commands between then and exit repeat . If other commands are included, an end if is needed.

Continuing with our time-billing example, the fickle partners have now decided to increase everyone's fees by 10 percent in the first three categories (fields) but to bump only the partners' fees by 25 percent in all remaining categories. The following loop handles the assignment neatly:

repeat with x = 1 to number of fields

put field x * 1.1 into field x

if field "Status" is not "Partner" then exit repea t

Page 193: Software at Speed of Sound -- Dan Shafer

168

if x > 3 then put field x * 1.25 into field x

end repeat

This loop would be part of a larger loop so that when the exit repeat command is encountered, the next card is examined and this repeat loop executes again.

Page 194: Software at Speed of Sound -- Dan Shafer

169

Chapter 9: Card and Stack Management Methods In this chapter, you'll learn how to use Transcript commands to:

• navigate among cards and stacks • find and mark cards in a stack based on their contents • manage multiple open stacks and their associated windows • enable the user to return quickly to some predetermined point in a stack • print one or more cards in a stack, one card per page

Navigation Commands Much of what I’ll have to say about navigation in this chapter applies with some specificity to one category of Revolution applications that I’ll call stackware. Stackware derives its name from the historical roots of Revolution in Apple Computer’s pioneering HyperCard. HyperCard documents, which acted in many ways like applications, were called “stacks” because HyperCard enforced a stack-of-cards metaphor in all of its creations.

Revolution transcends those roots but does not entirely abandon them. It is possible to create stackware in Revolution and, in fact, a great deal of work being done in Revolution looks and feels a lot like the old HyperCard stacks. A stack in HyperCard often consisted of dozens or even hundreds of cards, each of which corresponded roughly to a record in a database. The information displayed on those cards was contained within the stack itself, so that providing a user with a single stack file resulted in that user having the application and all relevant data. Revolution’s design honors that original concept but extends it quite dramatically so that even a stackware style of application will be designed quite differently in Revolution from what it would be in HyperCard.

In a typical stackware application, the user spends a lot of time moving among cards. The user generally controls this movement with buttons you've designed and connected to arrows and icons. But there are times when you want to control which cards the user accesses and in what order. Sometimes this need is temporary, and other times it is an integral part of the stack's design. When you do want to control the navigation process, you can use one of Transcript's built-in commands, possibly with one of the language's constants.

You can use three Transcript commands for navigation: go, open , and find . Use go to send the user to a specific card that can be named or referred to relative to the user's present position in the stack or application. (The open command is equivalent to go but it is recommended that we use go in lieu of open to navigate to cards and stacks. The open command is primarily for SuperCard compability.) Use the find command to send the user to a card based on its content.

Page 195: Software at Speed of Sound -- Dan Shafer

170

Using go in a Script You have probably had the experience of typing go commands into the Message box. This command can be used to move from your present position in the Revolution world to any card in the current stack, to the currently visible card in another open stack, or to any card in any other stack. Using the go command in a script is not substantially dif-ferent from using it in the Message box. The basic syntax of the command is simplicity itself:

go [to] <destination>

Notice that you can use the optional word to for more English-like syntax. The destination can be a card or a stack. You cannot go to a field or any other object.

Addressing a destination As you may know, any card can be addressed by name, sequence number, or unique ID number. If the seventh card in the current stack is named Directions and Revolution has assigned it the unique ID 14238, all of the following commands will take you to the card: go to card "Directions" go to card 7 go to card ID 14238

The argument to the go command can be stored in a container so that the addressing is indirect. If the container helpPlace contains the value Directions, this command takes you to the same card as the previous three commands: go to card helpPlace

If you want to go to a different stack from the current one, simply supply the name of the stack in quotation marks as an argument to the go command. In the absence of instructions to the contrary, Revolution goes to the first card in that stack: go to "My Appointments"

NOTE: There is not universal agreement among Revolution developers whether it should be necessary to put stack names in quotation marks as I’ve indicated here. If the stack name is all one word, you can get away without the quotation marks. If the name of your stack contains spaces, then you must put its name in quotation marks to use it with the go command. As a matter of routine, I put quotation marks around all literal values like stack names to avoid confusing them with variable names when I’m reading and modifying my work.

If you place the name or other identifier of the destination stack into a container, of course, then you need not use quotation marks at all; in fact, to do so would be an error. Note that to go to a stack, you need not use the word stack (though you may do so for readability). Revolution assumes you want it to change stacks when you issue the go command and only needs clarification if you want to go to a card in the current stack. If you have a card in your current stack with the same name as an open Revolution

Andrew
Highlight
Page 196: Software at Speed of Sound -- Dan Shafer

171

application stack, and type go followed by that name, Revolution will take you to the stack every time. You can combine these methods of navigation to take the user to a specific card in a stack: go to card 78 of stack "Employees" go to card ID 41233 of "Help"

You can specify that the destination stack is to be opened in a new window or you can tell Revolution which currently open stack window’s contents should be replaced by the newly opened or displayed card or stack. The syntax for this command is one of the following depending on your intent:

go to [stack] <stackldentifier> in a new window go to [stack] <stackIdentifier> in “Old Stack”

The latter tells the stack to open in the window now used by the stack named “Old Stack.”

Cards with special addresses Several groups or classes of cards in any stack carry special addresses that can be used to make navigation more readable. These special addresses include:

• positional addresses like first , third , or last • relative addresses like back , recent , or next Revolution includes special constants to address these specific cards. The positional constants include the ordinal numbers 1-10 (first, second, third, and so on) as well as the random card address any and the address of the last card in any stack, last . The middle card in the current stack can also be addressed specially, as mid or middle , should you come up with a need to do so (I’ve never seen one!). All these special addresses are also used to address components of containers. Their use in that context is more fully discussed in Chapter 10. All of the following, then, are valid go commands: go to seventh card — same as "go card 7" go to last card — go to the last card in this stack go to the third card in "Ideas" go to last card in "Ideas" go to any card — pick a card at random and go there go to mid — go to the middle card in the current st ack

Notice that you can use the key word the in conjunction with these special card names because they are in fact functions. It is, as you’d expect, optional but you may find it makes your script more readable.

The relative constants include next , previous (also abbreviated prev ), forth , back , and recent . For all practical purposes, the last two are identical. The first two — next

Andrew
Highlight
Page 197: Software at Speed of Sound -- Dan Shafer

172

and previous — affect the user's position within the current stack relative to the present position. Thus, if the user is at the fourteenth card in the stack and a script executes the go previous command, the user is shown the thirteenth card in the stack even if the fourteenth card was reached without going through the thirteenth card. In other words, next and previous are not related to the route by which the user arrives at any given point.

On the other hand, back and recent are both path-related. As the user traverses a stack (or set of stacks), Revolution builds a list of each card visited. This list is called the Recent list. If the user has been taken or has "driven" through a path like that shown in Figure 9-1, each use of one of these commands backs up the path one step. The forth indicator is also path-related. It points at the next card in the Recent stack.

Figure 9-1. The back, next, and previous commands

As you can see in Figure 9-1, the user started at Card 1 of the stack called Test and followed the indicated path to reach Card 43 of the same stack. Now, a go prev command takes the user to Card 42 of that stack. A go next command takes the user to Card 44 of the stack. But a go back command (or a go recent card ) takes the user to Card 52 of the stack, and another execution of that command moves the user to Card 1 of the Home stack.

Nonexistent Stacks and Cards If a go command attempts to access a nonexistent card or stack, the message "card not found" will be returned in a function called the result . Good error-trapping strategy dictates that you check for this condition as in this example:

Andrew
Highlight
Page 198: Software at Speed of Sound -- Dan Shafer

173

go to card id 1437 if the result is empty then put field 1 into gorp else answer “Oops! No such card!” end if

You can also use the there is a(n) operator to determine in advance whether a particular card exists. See the discussion of this command in Chapter 8.

Stack Management Techniques You can go to a stack that is already open (but perhaps not visible to the user) or you can use this command to open a stack that has not yet been opened, whether it exists on the user’s local hard drive or somewhere on the network or the Internet. Note that open and go (or go to ) are synonyms; use them interchangeably.

If the stack you wish to open or go to is currently open but not visible to the user, it will be made visible and brought to the top of the Revolution environment or of your application, depending on how the stack is being run. In that case, you need only supply the name of the stack. If, however, the stack you wish to open or go to is not currently open, then you need to supply a full path to the stack file’s location (the .rev file). You can use a file path or the URL reference to the stack file. If Revolution can locate the stack file in question, it will open it to the mainstack of that file.

For example, to open a stack over a network using the HTTP protocol, you might write a line like this:

open stack URL “http://www.mysite.com/examples/test 1.rev”

Similarly, you can use the file form of the URL keyword

open stack URL “binfile:/Users/smith/Documents/RevA pps/test1.rev”

The latter is equivalent to:

open stack “/Users/smith/Documents/RevApps/test1.rev”

but many Revolution developers get into the habit of using the URL approach for all file activity. I’ll have much more to say about that subject in Chapter 10. Notice, too, that to open a Revolution stack, you must use the binfile protocol rather than just file . The latter is reserved for text files.

Andrew
Highlight
Page 199: Software at Speed of Sound -- Dan Shafer

174

Opening a Stack Invisibly Sometimes you’ll use a stack in a sort of background supporting role inside a Revolution application. For example, you might have a stack other than the mainstack that holds preference settings or which remembers user activity from a previous session. You may need to use scripts or data from such a stack but the user doesn’t need to be aware of its existence, let alone see it. To open a stack so that you can access its contents and run its scripts without the user seeing it, use the invisible property key word right after go or open , as in this example:

go invisible stack “Preferences”

The treatment of the stack is identical in all respects to that when the invisible keyword is omitted except that the stack has its visible property set to false. You can always show the stack later. But it is important to note that any go commands will not show the stack until you explicitly show it or set its visible property to true.

The Many Modes of a Stack In Revolution, you can create several types of stacks. At the most basic level, there are mainstacks and substacks . Each Revolution stack file has one and only one mainstack . This is the stack which is first shown when the file is opened (or, from the user’s perspective, the application is launched). All other stacks in a stack file are substacks . Any window in your Revolution application is typically a separate stack. Thus if you have information dialogs, preference sheets, and other such window types, your stack file will contain multiple stacks.

When you create a new stack (window) in the Revolution development environment, you give it a style. By default, all new windows have a style of topLevel . This type of window is always editable and is a typical general-purpose window in an application. The other styles of windows you can create in Revolution include:

• modeless

• modal

• palette

• sheet (OS X only)

• drawer (OS X only)

You can define the mode of a stack in any of three ways:

• at creation time by sending the stack a “set style” command

• at the time you use it, by opening it with a command other than go, which always open a window in whatever mode it was created, with topLevel remaining the default

• after it’s opened, by sending it a “set style” command

Page 200: Software at Speed of Sound -- Dan Shafer

175

A modeless dialog (or window) behaves essentially like a topLevel window except that it cannot be edited in the IDE. When you use a modeless dialog in a Revolution application, the user won’t necessarily be able to tell it from any other window.

A modal dialog, on the other hand, refuses to allow users to do anything in your application until they do something to close the dialog.

Be cautious using modal dialogs in the Runtime development environment. If you have a window open and you set its style to modal , you won’t have a way to return it to being a topLevel or modeless window unless you’ve already included the handler that reacts to mouse clicks and dismisses the window.

A palette window is something like a modal one, but it has a smaller title bar with no title in it and cannot be resized.

You can, as I’ve indicated, open a window in a specific mode. For example, if you have a palette window that is intended always to be used as one, you just set its style to palette when you create it and thereafter it will always open as a palette. However, if you have windows whose roles or modes change from time to time, you can define them to be what you need at runtime. Do this by using the appropriate mode name in place of the go or open command. This statement opens the window called “queryPalette” as a palette:

palette stack “queryPalette”

An alternative syntax for this operation is:

go to stack “queryPalette” as palette

Note, however, that if you’ve defined the stack “queryPalette” to have a style of anything other than topLevel , then its style will always take precedence over any attempt to force it to open in some other mode. So you will always want to leave windows in their default topLevel style if you intend ever to be able to open them in any other mode.

Stack Window Management The user can, of course, have more than one window open at a time. In fact, in many compiled stand-alone programs created in Revolution, the application opens many different windows. Each window can only be open once, and when it is opened it can either replace a previously opened stack window or open in a new window and add to the open windows on the screen.

Page 201: Software at Speed of Sound -- Dan Shafer

176

Your scripts must take this multi-stack situation into account. For example, they must manage the issue of whether a stack opened under script control will open in a new window or replace the current stack.

The function, the stacks , can be used to assist you in this process. It returns a return-delimited list of all stacks that have been open since Revolution or your application was launched. Unsaved stacks appear as blank lines. Stacks which have been opened and then closed during this session will also be listed unless their destroyStack property is set to true, in which case they will not appear. Any open Revolution windows are also included in the list.

You may also want to set or read the rectangle property to help the user manage the location and clutter associated with multiple open windows. Because they are properties rather than commands, and because they therefore follow the rules associated with the dozens of other properties in Transcript, we discuss them in Chapters 5 and 10 rather than here. But you should be aware that you sometimes need to manage these properties as part of assisting the user in managing his or her Revolution application environment.

There are also two system messages — moveStack and resizeStack — available to help you manage situations where multiple stacks may be opened. These messages may come in particularly handy when more than one stack is open. See Chapter 6 for a discussion of these window messages.

In addition, you can use the hide and show commands to change the visibility of any card window.

Finding Cards by Content Sometimes, you don't know the specific card you want to steer the user to, but you do know that it contains some key word or words. In those cases, use the find command to locate the desired card. This command is not mysterious. Even if your exposure to Revolution consists mostly in looking at and using other peoples applications, you are accustomed to using find commands to move to cards when you want to locate an individual in your Address application or an appointment in your Datebook application. Used within a script, the find command is identical, though you may use it with slightly more arguments and sophistication than you were aware was available to you in the Message box. The simplest form of the command is: find "<text>"

When it is used this way, the find command searches through all fields of each card in the current stack, beginning with the currently visible one, and locates the first occurrence of the string supplied as an argument.

You can add two types of qualifiers to the find command. The first type defines the nature of the search; the second confines the search to a specific field. You use both

Andrew
Highlight
Page 202: Software at Speed of Sound -- Dan Shafer

177

kinds of qualifiers extensively in scripting. Without these qualifiers, Revolution locates the text anywhere it occurs, even if it's split over two fields on the same card, as long as the target string is the beginning of a word. Thus, it would find an in answer and antidote, but not in banana or Alexander.

What kind of match to find? You can add any of four qualifiers to the find command to tell Revolution what constitutes a "hit": • whole • string • word • chars Each of these qualifiers results in a slightly different approach to the find command. They may not be used in combination with one another. Let’s take a look at each of these qualifiers in turn, then examine an example that clarifies how the qualifiers differ from one another.

The command find whole is the most targeted of these commands. Its rules for declaring a combination of characters as a "hit" are as follows: 1. The word or phrase found must match exactly the argument supplied with the command. 2. Spaces are significant. 3. Order is significant. 4. All of the word or phrase must appear in the same container on a card.

If you want Revolution to find the target string even if it occurs midword, use the find chars form of the command:

find chars "end" This command finds blender, ending, and weekend.

The find string command is essentially the same as the find chars command, except that when its argument has one or more spaces in it, Revolution can find an exact string that contains partial words.

If you want to limit the search to the whole word or words supplied as the search text, use the find word (or find words to improve readability as appropriate) form: find word "end"

This command only finds the word end as a whole word. The words blender, ending, and weekend are not located.

Andrew
Highlight
Andrew
Highlight
Page 203: Software at Speed of Sound -- Dan Shafer

178

Laboratory: Specifying the search

1. Create a new stack using the "New Mainstack" option from the File Menu. 2. Create two fields for this new stack. They can be small because we’re not going

to put much text into them.

3. Group these two fields by selecting them both and clicking the “Group” icon on the Revolution toolbar or “Group Selected” in the Object menu. Check the “Behave like a Background” checkbox near the bottom of the Group’s property inspector.

4. On Card 1, type in field 1 the word "Irving" and in field 2 the word "Glotzbach".

5. Create a new card, Card 2, and type in field 1 the words "Irving Glotzbach" and in field 2 the words "lives here".

6. Create a new card, Card 3, and type in field 1 the words "Ingemar Johansen" and in field 2 the words "lived here".

7. Save your work. 8. Go to Card 1.

9. In the Message box, type: find "Irving Glotzbach"

10. After Revolution finds the string on Card 1 (notice that it appears split across two fields), press the Return key. Notice that it finds the string on Card 2 as well. Press Return again and Revolution returns to Card 1.

11. Now try typing: find words "Irving Glotzbach"

12. Notice that the results match those we encountered in Steps 7 and 8. 13. Change the Message box so that it reads:

find whole "Irving Glotzbach"

14. Notice that now Revolution only finds Card 2. This is the only card where the target string, "Irving Glotzbach", exists exactly as provided in the argument to the find command and entirely in one field.

15. Now type the following line into the Message box: find whole "live”

16. Notice that Revolution simply reports “not found” in the lower portion of the Message Box; it cannot find the word "live" as a complete, stand-alone word. (You can achieve the same result with the find word variation in this case.)

17. Return to the Message box and type: find chars "ing"

Page 204: Software at Speed of Sound -- Dan Shafer

179

18. Now Revolution finds all three cards, because all of them have the string ing somewhere in one of their fields, though not necessarily at the beginning of a word.

19. Let's change the Message box to read: find "ing"

20. Now Revolution only finds Card 3, because that's the only place that the string "ing" begins a word (in this case, Ingemar).

Narrowing the search When you design stacks, each container in a group will probably have a specific purpose. Each card on which the field appears will store the same kind of information in that field. On an inventory stack's cards, for example, you might store the part number in Field 1, the quantity on hand in Field 2, the price per unit in Field 3, and the date last ordered in Field 4. If you want to find all parts with a 5 in the number, you can write: find "5"

But Revolution would find every card where the quantity on hand was 5 or 500, any part that cost $59.95, any date that started with a 5 — in short, it would find every word starting with “5” in every field on every card of the stack. That's not what you wanted. So you might narrow the search to Field 1 like this: find "5" in field “QuantityOnHand”

Revolution would ignore the contents of all other fields and concentrate its search for values starting with “5” only in the identified field. Instead of searching the quantity field, perhaps you want to find a part number that has a “5” in it. If the 5 were a part of the part number that is separated from other numbers so that it qualifies as a word in Revolution, then you could even use: find word "5" in field “partNumber”

This is a little more specific, but it will still find any 5 surrounded by spaces and punctuation anywhere in Field “partNumber”.

You can further confine the extent of a search undertaken by Revolution in response to a find command. Every card and group has a property called dontSearch . By setting this property's value to true for a particular card or background, you can cause Revolution not to search the fields in this card or background when a find command is issued. You can set this property in one of two ways, as is the case with most of the often-used properties in Revolution. You can use a script:

set the dontSearch of Group 1 to true

or you can edit the properties in the Group’s inspector window and check the checkbox labeled “Find command ignores.”

Page 205: Software at Speed of Sound -- Dan Shafer

180

(Note that if you use a script, the checkbox in the object inspector may not immediately update to reflect the change. The change is nonetheless made.)

Since this property can be set and reset (to its default value of false) from your script, you can selectively turn searching on or off for particular cards and backgrounds quite easily.

You can also confine your find operation so that it operates only on cards that have been previously marked. (Card marking operations are the subject of the next major section of this chapter.) The syntax for this version of the find command is as follows:

find <whatToFind> [in field <fieldldentifier>] of m arked cards

Limitations on find Unfortunately, the find command cannot be forced to confine itself to only a specific element in a field. You may know that the 5 you are interested in is always the third digit, but that's not going to help you with the find command. In that case, design a customized search routine that looks at the third character of the field and does something if it's a 5.

When you execute the find command within a script, the user cannot continue the search with a simple Return key, though you can design the script to make this possible.

When Text Isn’t Found If the text you search for using any of the variations of the find command we’ve looked at is not located, the special system function the result returns a value of “not found.” In fact, if the result is empty, then you know you’ve had a successful find ; if it’s not empty, then you have searched for a string that can’t be found for one reason or another.

A handler fragment like this is generally what you want to use when you want to be sure the text being searched for is found:

find “something interesting” if the result is “not found” then -- could also use “is not empty” as the test -- some processing to notify the user of the error else -- continue with the script

Case Sensitivity and find Operations By default, Revolution find operations are not case-sensitive. If you wish to make a particular search case-sensitive, set the caseSensitive local property to true before you conduct the search. Because the setting only remains in effect while the current

Page 206: Software at Speed of Sound -- Dan Shafer

181

handler is executing, it’s not necessary to reset caseSensitive to false when you finish a search; although it’s probably good scripting practice.

on mouseUp set the caseSensitive to true find “EdInBuRgH” in field 3 set the caseSensitive to false -- not necessary end mouseUp

The found card When the find command is successful, it makes the card on which it locates the match the current card. You can then use property-retrieval techniques to find out that card's ID, or examine other fields or parts of the same field to qualify the card before continuing processing. If the find command fails, it leaves the current card unchanged. Once a find operation has located a "hit," you can use functions described in Chapter 10 to retrieve the text or the surrounding information.

After the find is Over When a find operation locates the search string, it displays the card on which the hit is found, and puts a rectangle around the text. If the user were executing the find operation from the Message Box, then they could just press Return to go to the next text meeting the criterion. However, if the find operation is contained in a script, then the script is responsible for providing a way for the user to search for the next occurrence of the text. This is generally handled by initiating the script from one stack that is a custom find dialog box including a “Find Next” button.

If, after a hit has occurred, you want to erase the application’s recollection of the most recent successful find operation, including removing the rectangle from around the found text, you can issue a find empty command.

Marking Cards for Later Use A close cousin of the find command is the mark command. Both of these commands can be used to locate information in a card or stack by its content, as explained in the preceding section. The major difference between the two operations is that find stops on any card where it finds the target text while mark notes that a particular card contains the desired text and goes on searching the rest of the stack. When it has found all the cards with "hits," it stops. You can then work with all of the marked cards as a group in several interesting ways.

Criteria for card marking You can use any logical expression (see Chapter 8 for a discussion of logical expressions in Transcript) as a criterion for marking cards. This means you can use such text-locating operators as is , is in , and contains , among others, to mark a group of cards with related data. You can also mark cards based on criteria unrelated to the contents of their fields, perhaps based on the outcome of some calculation.

Andrew
Highlight
Page 207: Software at Speed of Sound -- Dan Shafer

182

For example, let's say you have a stack of cards containing information about the compact discs in your prized collection of jazz music. You want to look through all the CDs that you've described in a field called "mood" as being "upbeat." You can use a command like this to accomplish the purpose: mark cards where field "mood" contains "upbeat"

You can use logical connectors and , or , and not in describing the criteria for marking cards. For example, assume that you only want to listen to relatively recent upbeat jazz CDs tonight. Then you could write a line like this: mark cards where field "mood" contains "upbeat" and field "year" > 1988

You can also use the power of the find command to aid you in marking cards in your stack. The syntax for this variation of the mark command looks like this: mark cards by finding <text>

This is useful when you are less concerned with where information appears in a card (as you must be able to specify with the mark where variation) than with its existence on a card somewhere. In addition to marking cards, you can also unmark them. The most obvious use of this capability arises when you want to clean up after yourself when you close a stack and want to be ready to mark cards the next time the user runs your application. In that case, just put the following line into an appropriate handler:

unmark all cards

Another way to use this ability is to narrow the collection of cards marked by a command. For example, let's assume that you've carried out the second of the mark commands previously cited and discover that you still have too many CDs on your list to play tonight. But you're not particularly in a mood to hear violins tonight. With the existing set of marked cards, you can carry out this kind of operation: unmark cards where field instrument contains "violi n"

Now the only marked cards will be those where all three criteria are met: • mood is upbeat • year of release is later than 1988 • there are no violins on the CD

What to do with marked cards Once you've marked a group of cards, you can modify several frequently used Transcript commands to work on only the marked cards. For example, all navigational commands can be modified so that you see only marked cards: go to next marked card go to last marked card

Andrew
Highlight
Page 208: Software at Speed of Sound -- Dan Shafer

183

Because we can find out how many cards are marked, we can set up repeat loops to operate only on those cards: on mouseup go first marked card repeat number of marked cards times put 1.1 * field "Salary" into field "Salary" go next marked card end repeat end mouseUp

The other operations that you can perform on marked cards as a group include • show marked cards • print marked cards

I’ll have a bit more to say on this subject at the end of this chapter.

Using pop and push in Scripts In complex scripts with lots of card movement, you often want to mark a card as one to return to after some exploration is complete. Rather than requiring you to keep track of how many go back commands it will take to get there, Transcript includes the pop and push commands. To oversimplify a bit, push marks a card for later instant retrieval with the pop command. The push command does not require any parameters. (You may, for the usual reason of readability, write the command to push the current card as push card , but that is not necessary.) If you want to mark the current card, simply use the command by itself. If you want to push the most recent card (that is, the card from which the user got to the current card), you need to add a parameter:

push recent card

The pop card command indicates that you want to return the user to the card to which you most recently applied a push command. The card parameter is required. The only other use of pop is explained later when we describe multiple push statements.

The stack created by push You can think of the push command as moving the card to which it applies to a special location that in traditional programming terms would be called a stack. Only this is not a Revolution stack. Rather, it is a kind of single-file line in which cards that are pushed stand until they are called again. In such a situation, only the top card can be affected by a pop command. It must "get out of the way" before you can release any of the cards placed there earlier.

This kind of special location is called, in computer terms, a LIFO stack. LIFO is an acronym for "last in, first out." The last card pushed into this line is the first one popped

Andrew
Highlight
Page 209: Software at Speed of Sound -- Dan Shafer

184

out of it. Most of the time, your scripts will not push more than one card at a time. Only very complex applications ever need to handle multiple-card special stacks.

Using pop without showing the card But if you do have an application that uses multiple-card pushes and you want to pop a card other than the top one into view, you will need to use the technique of popping the card into a container. The syntax for this technique is: pop card into <container>

When you pop a card into a container, the card's long ID is placed into the container and the card is removed from the special stack location without being shown to the user. Later, you can use the information in the container, along with a go command, to display the card that was popped into the container if you wish. For example, assume you have executed two push commands. The first one operated on Card 6 of the current stack and the second operated on Card 157. Now you want to take the user to the card that was pushed first. The basic handler segment looks like this: pop card into field 1 of card “holder” pop card

Now the card identifying information for Card 6 is stored in the container called holder, in Card Field 1. If you want to show that card, use a command like this: go to card field 1 of card “holder”

Notice that this does not take the user to the field but rather to the card whose identifier is stored in the field.

Using push and pop between objects You can freely mix the sources of push and pop commands; there is only one stack onto which cards are placed and from which they are removed, regardless of whether they are pushed by a user action or a script action.

You'll find that judicious use of push and pop can save you a great deal of unnecessary navigation.

For example, let’s say that you have designed an application in which the user clicks on a button to receive help with some operation. The user may wander fairly far afield in learning what they need to know about the situation. When they’re done, they’d expect to be returned to the original location where they asked for the help. For a variety of reasons, this card may no longer be the visible current card in its stack. But if you push that card before the user goes wandering off, a simple pop will bring the user quickly and comfortably back to resume working with your application.

Page 210: Software at Speed of Sound -- Dan Shafer

185

This kind of usage might be particularly useful in an educational, training or tutorial application where answering the user’s question might require the program to take the user to a card elsewhere in the stack.

By the way, this whole notion of having a stack of cards to manipulate so easily and directly is one of the real joys of a card-and-stack based metaphor like the one used in Revolution.

Printing Cards: The Basics Revolution supports fairly sophisticated printing and report-generation capabilities. Those topics are more advanced than this volume and are treated in a separate eBook in this series.

But sometimes your printing needs are as simple as printing out a set of pages with each page containing a single Revolution window or card or, if the cards are small enough, multiple cards per page.

The simplest form of the print command is:

print card

This will print the current card to the appropriate output device (which is somewhat system-dependent) with no intervening confirmation dialog. This means, among other things, the user is not asked to select a printer; the application uses the default printer on Mac OS and on Windows, but creates a Postscript file on Unix systems and then calls the program specified in the Revolution printCommand property.

You can print multiple cards to the printer by using a variation on the print command:

print stack print marked cards print 13 cards

Depending on the size of the cards, Revolution may print multiple cards per page using these techniques, in the absence of contrary instructions.

You can gain some control over the size and placement of the printed cards by using two additional options with the print command: setting up a page size into which to print the card(s); and defining the precise area of the cards themselves to print.

By default, Revolution assumes you want to use the full page size of your printer and that you want to print all that is visible on each card. This means, e.g., that if you have a card with a scrolling field on it, only the contents of the scrolling field that are visible

Page 211: Software at Speed of Sound -- Dan Shafer

186

when the print command is executed will be printed, unless you do something to override that behavior.

To define a smaller portion of the paper in which to print, you must use the into operator and supply four comma-separated digits that define the top, left, bottom and right edges, respectively of the rectangle into which you want to print. These measurements are given in pixels; there are 72 pixels to an inch. So if you want a two-inch white space border around the printout on each page, you’d define the command this way:

print stack into 144, 144, 144, 144

As I said a moment ago, Revolution prints only as much of the card as is visible in its window. If you have controls, particularly scrolling fields, that appear outside the card’s boundaries, you have two choices if you want to print that hidden content. You can either enlarge the stack window to show all of what you want printed or you can use the from keyword option in the print command and define an upper left corner and a lower right corner that define the area to be printed.

Let’s say, for example, that you have a card that is 400 pixels wide and 360 pixels deep. It contains a scrolling field that is 600 pixels deep when it is expanded large enough to show everything it contains. In that case, you can write something like this:

print this card from 0,0 to 400,600

If you also supply a rectangle into which to print, Revolution will scale the output to accommodate both the rectangle from which you want to print and the rectangle into which printing is taking place.

print this card from 0,0 to 400,600 into 144, 144, 144, 144

Notice that the print command, used as described in this section, prints everything that is visible on a card. This includes buttons and other controls. If you don’t want them included in the printout, you should first hide any controls you don’t wish to print, then show them when you’re done. This is best handled in a repeat loop (see Chapter 8).

To print multiple cards but not the entire stack, you should first open printing , then loop through or provide a command for the cards to print, and then close printing , as shown here:

on mouseUp open printing print 12 cards -- beginning with the current card close printing

Page 212: Software at Speed of Sound -- Dan Shafer

187

end mouseUp

Page 213: Software at Speed of Sound -- Dan Shafer

188

Chapter 10: Managing Text and Data Without Database s In this chapter, you'll learn how to • read the contents of fields • get information about data in fields • find and select field contents, including advanced search using matchText • modify the contents of fields

• work with styled (rich-formatted) text • sort cards by the contents of fields • use text in fields to create hypertext applications • deal with date and time data as a special class of information • trap the use of the Return and Enter keys in a field

• access and use external text files as “poor person’s databases”

Revolution as an Information Base Many, if not most, Revolution applications tend to be fairly data-centric. They are akin to database applications in that their primary mission is to support the user’s need to retrieve, modify, manipulate, and add to information. This is not to say Revolution can be used only for such applications; far from it. Revolution has been used to develop games, multimedia-rich applications, system utilities, and even programs that run on servers performing complex and obtuse behind-the-scenes tasks.

But our focus in this chapter will be on Revolution as an information base. I should be clear about a couple of things at the outset.

First, Revolution is not, strictly speaking, a database management system even though it can be made to function as one on a relatively small scale. Revolution does allow you to create user interfaces that access serious databases stored in freeware and commercial database packages, but that subject is beyond the scope of this discussion. To characterize Revolution as a database management systems is inaccurate for at least two reasons:

• Revolution does much more than manage data, which is all database management systems do.

• Database management systems have capabilities for data manipulation that exceed those of Revolution.

Nonetheless, Revolution does manage data. Its management of information through hypertext features (the links between cards and stacks that make authoring without Transcript more powerful than many kinds of programming) and Transcript scripts is its

Page 214: Software at Speed of Sound -- Dan Shafer

189

most important feature. If you combine the find, mark, and select groups of commands, you end up with a pretty powerful data manager with almost infinite extensibility.

Revolution also has a text style called grouped that permits you to join multiword phrases into what, for many purposes, can be treated as a single word.

In this chapter, we examine Transcript commands that manage information. This data can be stored in any Revolution container, including:

• fields

• variables

• custom properties of Revolution objects

• URLs

• buttons

• images

This chapter focuses primarily on data stored in fields because fields are the most natural and obvious places to store changeable data and the only place you can store such data where the user can change the information. We’ve already used variables extensively in this book and they will continue to play an important role in your Transcript programming, but they are not the primary place to store information for user management.

The other place data is frequently stored for user management in Revolution is in external text files. I’ll cover that subject in the last portion of this chapter. Between fields and text files, Revolution can be an effective and powerful data management application-building tool.

A Word About Main Stacks and Substacks The stack you designate as your application’s mainstack will be treated as an application when you create a distribution of it for others to use. A running application cannot be modified on any system but the Macintosh. To create a consistent approach to mainstack usage across all platforms, Revolution is designed so that any changes you make to your mainstack while it is running as an application cannot be saved. Of course, while you are editing the mainstack in the Revolution IDE, you can change it to your heart’s content.

This is a big departure from Revolution’s ancestor, HyperCard, but unless you are an experienced HyperCard developer, it won’t matter much to you.

In Revolution, then, if you are storing data internally to the program, you must use at least two stacks, one main stack that is what the user launches when opening your application and one other stack that can act as a data stack where changeable information can be stored. Most Revolution developers accomplish this by the simple

Page 215: Software at Speed of Sound -- Dan Shafer

190

expedient of making their main stack a sort of “splash screen” that appears when the user launches the application and then disappears to give way to the substack where the brains and operations of the application reside.

NOTE: When you build a standalone application from a multi-stack Revolution stack file, you’ll need to tell Revolution’s IDE that you want your substacks moved into individual stack files when you choose the settings for the construction of your standalone. There is a setting in the Standalone Builder, accessed from the File Menu | Standalone Application Settings... option to do this.

However you choose to handle it, you must be sure that if you are creating Revolution applications where data is a key element and the user (or scripts or other methods) must be able to change that data, you must either create at least a two-stack application or use an external file which is not a Revolution stack to hold the data. Later in this chapter, I discuss both design approaches and provide some examples of each.

Reading Information from a Field When information is stored in a field, it can be obtained by the get command. This command is not new to us. We've used it in several Laboratory experiments and in other discussions. But now we will take a close look at how it works and gain more insight into its use.

We can also use the put command to retrieve data, even though the action of retrieval sounds contrary to what we usually think of when we use the word put. We can use put to copy information from a field to another field or a container. The put command also serves as the means for permanently modifying information in a field. We'll talk about the put command several times from several different perspectives in this chapter.

The get command As you may recall, get retrieves data and puts the result into the special It variable. From

there, your script can access It and do whatever your script wants, including:

• test the contents of It and use the test result in an if construct • use put to move data into another temporary container or field • modify data in It and then perform another function

Generally, if your plan for the data after it is retrieved involves moving it from It to any other container or field, it is better just to use put (discussed next) to move it directly. This is true for two reasons: • This approach requires fewer keystrokes and is a more efficient use of memory. • The It variable is used extensively by Revolution itself, so minimizing its use in your scripts is a good idea. At the very least, you must be careful not to put something into It

Andrew
Highlight
Page 216: Software at Speed of Sound -- Dan Shafer

191

and then call a Revolution function or command that replaces those contents. The resulting confusion can be disconcerting, to say the least.

Addressing reminder As you may recall from our previous discussions, particularly in Chapter 5, data in fields can be addressed in many ways to gain access to very specific information chunks. In the remainder of this discussion, we frequently use the item, line, word, and char operators to focus on the information we want to retrieve.

One major use of the get command is to obtain information in fields. You can retrieve all of a field's contents or the contents of some chunk of a field with commands like these: get field 1 get field 3 of card "Test 2" get first word of line 2 of field 1 of card "Test 2 " get char 3 to 6 of word 2 of line 2 of field 1 of c ard "Test 2"

In each of these cases, when the get command has been executed, the contents of the respective field or chunk expression are stored in the special global variable It. You can then use this variable's contents in whatever way is appropriate.

To see these retrieved values, you will have to use the put it command in the script or type it in the Message box. Recognize, however, that the latter approach can only be used to view the most recent retrieved data since the data changes with each new get operation.

If the argument that precedes a to expression is larger than the value that follows the to, you will only extract a single element of the object. For example, a statement like

get char 7 to 0 of word 2 of line 2 of field 1

will return only the seventh character of the second word on the second line of the field called field 1.

The other major use of the get command is obtaining values associated with Revolution properties. The get command is also used to retrieve the values of certain functions such as the time and date. This use of the command is explored later in this chapter.

Using put commands to read data Because the get command always puts its results into the special It variable, we need an alternate way of retrieving information from a card field. The put command is used for this purpose. You may recall from our discussion of this command in Chapter 5 that it has two basic forms:

• With only a source argument, put places the source into the Message Box in the Revolution IDE. In a standalone application, put with no destination does nothing.

Andrew
Highlight
Page 217: Software at Speed of Sound -- Dan Shafer

192

• With both a source and a destination argument, put places the contents of the source at a specified point in the destination container.

Typical uses for put In general, you can think of put as having two fundamental uses, corresponding to its basic forms.

First, it can be used to display the contents of containers so the user can see them. The contents of these containers can be put into the Message box, in which case the destination may be omitted (though it is probably best to include it for readability), or into visible fields. Sometimes the source argument to the put command is an expression, and the result of its evaluation is placed in the destination. Here are some examples: put "Hello, world" into card field 1 put 3.14*23.2 into card field "circumference" put return & "new line of text" after field "tracki ng"

The second use of put is to alter the contents of variables and invisible containers. In this case, the purpose of the command is not to let the user see the result of an operation but to place the result into a temporary or semi-permanent storage area. For example, if you want to make sure that a variable called “nut” has the value “Macadamia,” you would write:

put “Macadamia” into nut

How Many Characters in the Field? The length function can tell you how many characters are in any string, including any addressable component of a field. This is often useful in reports, in using the offset function (described in the next section), and in ensuring that user-entered data meets any length limits your script must impose.

The length function, like all Transcript functions, has two forms. The first spells out the command in full, readable English: the length of line 1 of field 1 of card "Test 2"

The second form uses an abbreviated approach: length (line 1 of field 1 of card "Test 2")

The two forms are interchangeable. Your choice of which to use will be governed by your experience and the need for script readability.

In either case, you should know that if you apply the length function to a full line in a field, the function does not count the carriage return at the end. This becomes particularly important when you use the offset function, because that function does count the carriage return.

Page 218: Software at Speed of Sound -- Dan Shafer

193

Finding and Selecting Text Not only can you use find to locate text to be dealt with in a Revolution application, but you can also take advantage of the ability to: • determine precisely where the text was found • select text based on the results of a find operation or on the text's location in a field, or even to position the cursor in a field before or after a particular location

Where did the find take place? Let's look first at the use of several functions to determine where a particular piece of text for which we have searched has been found. When a find operation is successful, Revolution returns the location of the found text in four special functions: • the foundText, which returns the characters enclosed in the box after the find command has located the text • the foundField, which returns the identification of the field in which the text was located • the foundLine, which returns the number of the line in the field in which the text was found • the foundChunk, which returns the complete chunking expression to indicate where the text was found For example, if we had a field containing the sentence, “The range of the radar can be set by the operator” and we performed a find operation such as: find "ran" the foundText would contain the word range (because with the unadorned find command Revolution locates the first occurrence of the string at the beginning of a word, then makes the entire word the found text). On the other hand, the foundChunk would contain the value char 5 to 9 of field 1, whereas the foundField would point to field 1 and the foundLine to line 1 of field 1.

If we were to change the search from find "ran " to find whole "range of", we would see different results. Because of the use of the whole qualifier, Revolution would return "range of" with the foundText function and appropriate values in the other functions.

Using select to Highlight Arbitrary Text Another useful text-manipulation command in Transcript is select . This command can be used to make any text chunk the currently selected text in a field or other container. You can select any arbitrary text that you can address. Both of the following operations, for example, select and highlight the words "range of" in our earlier example: select word 2 to 3 of line 1 of field 1 select char 4 to 12 of field 1

Using select to position the cursor Besides selecting text, you can use the select command to place the cursor precisely where you want it in a field. This means you can put the user in the right location to enter the next piece of information. You accomplish this with the select before and

Page 219: Software at Speed of Sound -- Dan Shafer

194

select after combinations. They can be followed by any chunking expression. Here are some examples: select after last line of card field 1 -- puts cursor at end of field select before first line of card field 1 -- puts cursor at start of field select after word 3 of line 2 of card field 1 select before char 5 of word 3 of line 2 of card fi eld 1

Selecting everything and nothing You can also use the select command with the text of parameter to put the entire contents of a field into the selection. For example, the line select text of field "testing" would select all of the contents of the field called "testing" and put them into the selection. The final form of the select command removes any existing selection. To do this, type: select empty

Combining select and find for data management You should realize by now that the intelligent use of combinations of the find command and the select command in Revolution can lead to powerful data management operations in Revolution.

By using the proper qualifiers on the find command, you can narrow the search. With the functions that return the precise location of a successful search, you can locate the targeted text with ease. Then you can select that text and copy it, move it around, replace it, and otherwise manipulate it.

Locating a sub-string's position with offset As you may recall from our discussion of the find command in Chapter 9, locating a card that contains a given string of text in a specific field or anywhere on the card is straightforward. But sometimes we need to know where a particular text string is located in a field by its relation to other known characters or strings. This is quite often useful in decoding formatted product codes and numbers, for example. To help you carry out this assignment, Transcript includes the offset function. This function takes two strings as arguments and returns a number indicating the character position of the beginning of the first string in the second string. The syntax is: offset (<stringl>,<string2>) Either argument (or both) can be a string enclosed in quotation marks or the address or name of a container that holds a string.

If Field 1 contains the string "Blue Rondo a la Turk" and the variable lookFor contains the string "Rondo," then offset("Rondo",field 1) and offset(lookFor,field 1) return the same answer: 6. This identifies the starting position of the source string (stringl) in the target string (string2). (Of course, you must do something with the

Page 220: Software at Speed of Sound -- Dan Shafer

195

returned value of the offset function. Normally, you will either put it somewhere or test it with an if clause.)

A practical use for the offset function may not be immediately evident. Let's look at an example using our inventory stack analogy. After you work through this example, you will undoubtedly see many places in your applications that you can use the offset function to great advantage.

Assume that the part number field in your inventory stack consists of two or more characters, followed by a hyphen, followed by three to ten additional characters (e.g., AB23-00984). The first segment of the code, up to the hyphen, is the Category Code, which tells you the type of the part. The second portion of the part number, from the character to the right of the hyphen to the end of the code, is your company's internal part number. There are many places where you want to separate those two segments of the part code and use them independently. Let's do a short Laboratory exercise to see how to do this.

Laboratory: Using offset for field decomposition

Create a new card in a Laboratory stack (or in a new stack if you prefer). Name the card Inventory 1. Now follow these directions:

1. Create a new field for this card. Call it Part Number.

2. Get into Browse mode. 3. Type into the Part Number field the following text: 1. AB23-00984 4. Create a new button. 5. Open the button's script-editing window by one of the usual methods.

6. Type in the following handler: on mouseUp global category, partNum put length(line 1 of card field "Part Number") int o fldLength put line 1 of card field "Part Number" into temp put offset("-",temp) into divider put char 1 to divider-1 of temp into category put char divider+1 to fldLength of temp into partN um end mouseUp

(You may be wondering why we put global declarations in this handler when we have never done this before. Because we will want to examine the contents of these variables in the Message box and because the Message box is outside the scope of the handler, the variables must be declared global. Otherwise, the variables will cease to exist after the handler finishes and Revolution will be unable to retrieve their values when you type their names into the Message box.)

7. Get back into Browse mode. Press the button to which the new script is attached.

8. Open the Message box if it isn't already visible.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 221: Software at Speed of Sound -- Dan Shafer

196

9. Type category and press Return. The Message box contains AB23. (See Figure 10-1.)

10. Type partNum and press Return. The Message box now contains 00984. 11. Change the contents of the Part Number field so that a different number of

characters appear before and after the hyphen. Then retry the button until you are comfortable with how this process works.

Figure 10-1. Category code portion of part number

Advanced Search Using matchText Before moving on with the subject of what to do with text located with the find or offset operators, I want to spend a few moments looking at a much more advanced searching technique embodied in the matchText function. While a discussion of this concept is well beyond the scope of this chapter, a brief mention here will help you to decide if you want to explore this function further with the Revolution online documentation.

The matchText function is Revolution’s way of dealing with the well-known computer science tool called the “regular expression.” A regular expression is a string of characters that defines arbitrarily complex strings by a process of pattern-matching. You use the matchText function using the following basic syntax: matchText(string,regularExpression[,foundTextVarsLi st])

The function returns true if the regularExpression is found in the string and false if it is not found. The use of the third parameter is beyond the scope of our discussion here.

The following example determines whether a given string contains three digits followed by a hyphen followed by two digits followed by another hyphen and ending with four digits: a U.S. Social Security Account Number:

matchText(ssaNumber,"[0-9]{3}+-[0-9]{2}+-[0-9]{4}+” )

As you can already see, regular expression syntax is the ultimate in terseness, which makes such expressions exceedingly difficult to parse visually. On the other hand, writing Transcript code that would analyze a string like 123-45-5432 to determine if it was a validly formatted Social Security number would take considerably more than one line of code containing just over 50 characters.

The fact that matchText only tells you whether or not a string matches a pattern would make it somewhat useless, but the third parameter actually allows you to collect into one

Page 222: Software at Speed of Sound -- Dan Shafer

197

or more variables the parsed-out portions of the string. This makes for a significant amount of power in the function once you understand how to use it.

If you’re facing a design need for which matchText seems like a good solution, I recommend you take the time to learn regular expressions. There are a number of good books on the market for doing so. Two of my favorites are Sams Teach Yourself Regular Expressions in 24 Hours by Ben Forta, and Mastering Regular Expressions by Jeffrey E. F. Friedl. The first one is a lighter treatment and not as penetrating and is suitable if you just want to get a clear basic idea of how to use regular expressions; the second is for more serious, in-depth study.

NOTE: Revolution includes a decent regular expression syntax guide on the Help menu under the “Quick Reference Guides” option.

Modifying the Contents of Fields It will come as no surprise that the primary command for changing the contents of a field is put. This versatile command — arguably the most often used in Transcript — can replace the contents of all or part of a field or place new information at any addressable place in a field. But put is not the only command that modifies the contents of fields. Two others, less often used but occasionally helpful, are type and delete. We've already described the put command, so this discussion will focus on the other two field-modifying commands.

The type command The type command works very much like the put command, with two key exceptions:

• The type command does not include the ability to address a container as part of its structure. There is no equivalent of the put into command (or before or after, for that matter) for the type command.

• The put command places data into the field as a single block of text, but type places data into the field as if it were being entered by a fast typist.

These limitations make the type command less than a critically important tool in your Transcript vocabulary. But there are two occasions when it may prove useful. First, if you are creating a self-running demonstration of a stack and want to simulate for the user the manual entry of text into a field, the type command works exactly as desired. To put text into a field, you must be sure the cursor is in the field before you type anything into it. This can be accomplished with the click command or with the select before, select after, or select empty commands discussed elsewhere in this chapter.

This makes it possible but somewhat unpredictable to experiment with type from inside the Message box. If, for example, you type, type "Hello, there, world!"

Page 223: Software at Speed of Sound -- Dan Shafer

198

into the Message box, Revolution will select the first field on the currently open card and type the text into that message. This can be disconcerting if it’s not what you intended, which it usually is not! If you type that same command into the Message box with no field available, nothing will happen.

Second, the type command is a convenient way of sending certain characters to a field from within a script. For example, you can write a group or stack script that types a Tab character each time a card opens. As a result, each time the user opens a card, the cursor is placed in the first field on the card.

The type command can also include modifier keys:

type “R” with commandKey

results in whatever command has the keyboard shortcut Command-R. It isn’t necessary to position the cursor in a field for such use of the type command.

The speed at which characters appear in the field if you are using type to place text into a field is controlled by the local variable typingRate which by default is set to 100. The value you provide, which must be an integer, defines the number of milliseconds of delay between characters appearing.

The delete command One of the most powerful commands in Transcript is the version of the delete command that removes text from a field. You can erase any addressable component — characters, words, items, or lines — of text in a field on any card with this command. The deleted text is not placed into the Clipboard. To save the text for later, use a cut command.

If you use delete to cut an entire line of text from a field, Revolution removes the line and its accompanying carriage return, moving any lines below the deleted line up in the field. If you delete individual words, even the last one on a line, however, the carriage return remains in place unless you explicitly delete it as well.

If you use the delete command with no parameter, it deletes whatever is currently selected. If you want to delete a specific object or chunk of text, you’ll want to specify what you wish to delete in an argument sent with the delete command.

Laboratory: Using delete

For our first experiment with the delete command, let's use the Message box and a variable. Open the Message box if it is not already visible (it doesn't matter what card you are on) and then follow these directions:

1. Type the following line of text into the Message box; note that the semicolons are used to separate statements from one another:

global temp; put "one two three four five" into tem p and press the Return key.

Page 224: Software at Speed of Sound -- Dan Shafer

199

2. Now type this text into the Message box delete second word of temp; put temp

and press Return. The bottom pane of the Message Box should contain: one three four five.

3. Now let's set up a more elaborate experiment. Pick any Laboratory card with a field and at least one button. If you don't have a card with those objects with scripts you are ready to erase, create a new card. In the example, we'll refer to the field as TD1 (for Test Delete 1) to reduce the amount of typing.

4. Open the button's script-editing window in one of the usual ways. 5. Type in the following script and click the Apply button when you're done:

on mouseUp delete second word of line 1 of field "TD1" put "First deletion complete" wait 3 seconds delete third item of line 2 of field "TD1" put "Second deletion complete" wait 3 seconds delete char 3 to 7 of line 3 of field "TD1" put "Final deletion complete" end mouseUp 6. Get into Browse mode. 7. Type the following three lines of text into the field:

This is is a test

Do,you,see,see,how,it,works?

That onis a test

5. Select all the text in the field, and use the Copy Text option from the Edit menu to save it in the Clipboard. Then de-select the text. The screen now looks like Figure 10-2.

Figure 10-2. Second delete experiment ready to run

6. Press the button and watch the Message box closely. At each deletion, the script displays a message pointing out that the deletion was made and pauses so you can examine the effect. 7. If you want to repeat the experiment to watch it more closely, select all the text in the field, choose Paste Text from the Edit menu, and press the button again. Repeat this process as many times as you like. You can also modify the script between runs, perhaps

Page 225: Software at Speed of Sound -- Dan Shafer

200

increasing the amount of time the script pauses or deleting different components of the field.

If you know specifically the chunking expression that describes the information you wish to delete from a field, you can supply it directly:

delete word 5 to 13 of field “FunnyStory”

You may, of course, use any valid chunking expression.

Note that you can also use delete to remove objects from a Revolution application. That’s probably not what you intend, but it is possible. To avoid accidentally deleting fields, in particular, be sure that you have selected all the text of the field before issuing the delete command: select the text of field 1 delete

Changing Fields by Concatenation Combining seperate chunks of text into a single chunk is called "concatenation." Revolution has powerful text concatenation capabilities. Using these facilities, you can combine the contents of various containers, components of fields, and string or character constants to modify the contents of fields or other containers.

There are two text concatenation operators: & and &&. The only difference between them is that the latter adds a space between the concatenated objects and the former concatenates them exactly as it finds them. Thus put "test" & "ing" displays the word "testing" in the Message box, and put "test" && "ing" displays "test ing", with a space between the two strings, in the Message box. The items to be joined by concatenation can be any combination of addressable field components, actual strings, or containers of text. The resulting concatenated text can be placed into a field or into a variable using the put command. Or it can be displayed, tested, or manipulated in the same ways as any text field. There is no difference between a field created by user entry and one synthesized by concatenation.

Uses for concatenation There are dozens of places in scripts where it is useful to join two or more pieces of text. Three such possible uses are discussed here for illustration.

Recall our inventory example with the part number divided into two components separated by a hyphen. If you think about it for a moment, you might have the user enter the category code separately from the internal part code. In fact, it may be essential to do it that way given the normal operation of business. The user might know the

Page 226: Software at Speed of Sound -- Dan Shafer

201

internal part code but have no idea who the product is purchased from without checking another source of information during data entry.

To accommodate this need, create two global variables, for example, Ven and IPN (for Internal Part Number). After the user enters those values for a new part being added to inventory, your script would have a command like this one: put Ven & "-" & IPN into Field "Part Number" The result is the concatenation of the category code (for example, AYS29), a hyphen, and the internal part number (for example, 90087) so that the Part Number field contains the full part number: AYS29-90087.

Another use for concatenation is personalizing a card in a stack that is used by several people. Suppose three different people use your stack and all have personalized information stored in it. The cards are named in such a way that the person's last name forms the first part of the card name and the card type forms the second part. Thus, Yeats's expense card might be named YeatsExp. At the beginning of a session or when the user changes, your script asks the user for his or her name and saves the last name in a variable called User.

Now when a button is clicked to take the user to his or her expense account card, the script does a simple concatenation to get the name of the card:

go card User & "Exp"

Each user may be unaware that other people using the stack have similar cards to theirs. Yet this is far more efficient than creating separate stacks for everyone.

The last example involves the extraction and display of information from several different fields or even several different cards in such a way that the user only sees the result, not the work that goes into it. For example, a button called Summary on a card might indicate that a manager wants to see the employee's name, hire date, salary, and last review date. The stack's only job is to get those fields and put them into a special field. Using concatenation, it can do so with one simple put command: put field "Name" && field "Hire Date" && field "Sal ary" && field "Last Review" of card "Evaluations" into field “Employee Summary”

Special constants for concatenation In addition to text containers, fields, and strings, concatenation can also include built-in Transcript constants that represent special characters frequently needed in text streams. These special constants are • quote • return • space • tab

Page 227: Software at Speed of Sound -- Dan Shafer

202

• formfeed • lineFeed You can insert these special constants anywhere in a stream of text being concatenated. You probably won't have a lot of use for the space constant in view of the double-ampersand concatenation and the ability to place spaces inside literal strings. But the others will come in handy at one time or another.

The quote constant is particularly useful when you want to place a quotation mark into a field but are frustrated in your attempt to do so because Transcript sees the quotation mark as marking the end of a string. Simply embed the quote constant in the stream of text:

put "The" && quote & "boss" & quote && "is here." -- Message box shows: The "boss" is here. Inserting carriage returns to mark the end of one line and the beginning of another in a field is another good use for concatenating with special constants. The formFeed and lineFeed characters, as well as tab, are useful in telecommunications; a discussion of that topic is beyond the scope of this book.

Working with Styled Text Revolution fields can mix type fonts and styles. Each field has a default font and style, which can be set manually from the Field Inspector when or after the field is created. It is also possible to use any of several field properties to set or change this default text style information.

Any text entered into a field by any means — whether typed in manually by the user or placed into a field by a put command, for example — will have the default style and font information applied to it. You can, however, use chunking expressions to select a run of connected text and then change the style attributes of that particular selection.

The specific field properties involved in setting and changing the text style information for a particular field or any of its contents are • textFont • textStyle • textSize • textHeight

• textColor • textAlign For example, to change the default font used in a particular field, you can use a line like this: set the textFont of field "parts" to "Helvetica" When you do this, any text that held the previous default font for the field is automatically changed to the new font. Any text whose font has been explicitly altered, on the other hand, is unchanged by this command.

Page 228: Software at Speed of Sound -- Dan Shafer

203

The textAlign property always applies to the entire field’s contents. You cannot mix left-aligned and centered text in a single Revolution field.

One of the styles you can set is called link. This style can be applied to a run of text to cause Transcript to treat a string as a hyperlink. (I’ll cover this specific use of the link style later in this chapter.) To do this, just apply this textStyle to a string of two or more words, as in this example: set the textStyle of word 3 to 7 of card field 1 to link

NOTE: The above line should work if you pluralize “words” as well, but as of this writing, in Version 2.6.1 of Revolution, on OS X, that will produce a “missing chunk” error. The bug has been reported and if and when it’s fixed, I’ll update this eBook. Meanwhile, just use “word” even though it sounds funny.

The style group is a synonym for link and is maintained for historical purposes. You should use the link style in preference to the group style.

Using HTML and RTF Formatted Text Aside from its own admittedly limited text styling, Revolution “understands” partial subsets of two standard rich-text formatting specifications: HTML (the markup language of the Web) and RTF (Rich Text Format, a specification maintained by Microsoft but published for anyone to use).

These translations are handled by means of two properties of Revolution fields. The first is called the htmlText property. Its basic syntax is:

set the htmlText of [ chunk of] field to < htmlString>

In general, you’ll use this property for an entire field, but you can focus it on a specific chunk of a field’s contents if for some reason you need to do so. The htmlString should contain embedded HTML tags, which the above command would translate into Revolution-specific formatting. The htmlString may also contain a few HTML-like tags which Revolution added to the mix to support some of its unique formatting capabilities. But it is important to note that Revolution’s support for HTML formatting is pretty limited and not entirely standard. If you need full-blown HTML support in your Revolution applications, I strongly recommend Altuit’s altBrowser product which you can obtain at http://www.altuit.com.

You can read a Web page into a Revolution field with a command such as this:

set the htmlText of field 1 to URL “ http://www.somesite.com/default.html ”

The contents of the page will be loaded into memory and displayed in the destination field as accurately as possible given the limited subset of HTML tags Revolution supports. Any unsupported tag is ignored.

Page 229: Software at Speed of Sound -- Dan Shafer

204

Table 10-1 lists the HTML tags and Revolution custom tags supported by the htmlText property of a Revolution field. Those with an “x” in the “RR” column are not standard HTML tags.

Table 10-1. HTML and Revolution Markup Tags

Tag RR Use or Purpose

<p> </p> Encloses lines of text.

<sub> </sub> Subscripted text

<super> </super> Superscripted text

<i> </i> Italicized text

<b> </b> Bold text

<strike> </strike> Strike-through text

<u></u> Underlined text

<box> </box> X Encloses text whose textStyle is set to box

<threedbox> </threedbox> X Encloses text whose textStyle is set to threeDBox

<expanded></expanded> X Encloses text whose textStyle is set to expanded

<condensed></condensed> X Encloses text whose textStyle is set to condensed

<font> </font> Supports a partial subset of HTML <font> tags (see discussion in text)

<a> </a> Encloses text whose textStyle is set to link or whose linkText property is not empty

<img src=”imageSpecifier”> Replaces a character in the string whose imageSource property is not empty.

If you use the htmlText property only to read Web pages into your applications, you won’t need to worry about the four tags that are Revolution-specific because they will never appear on standard Web pages.

The <font> </font> tag set encloses text whose textFont, textSize, foregroundColor, backgroundColor or language differs from the default of the field to which the htmlText property is applied. In those cases, the text will be represented by tag sets like this:

<font face=”Courier”>code sample</font>

Page 230: Software at Speed of Sound -- Dan Shafer

205

The size attribute of the <font> tag provides the font’s size in points rather than using the seven standard HTML font size values. (Consult the online Revolution documentation for the htmlText property for additional technical observations.)

Similar to the htmlText property is the rtfText property. You deal with it in the same way you do the htmlText property, by setting a field’s rtfText property to true. When you do that, text displayed in the field interprets a tiny portion of the RTF formatting codes and styles the text appropriately. The types of codes supported are:

• colors

• styles (but not all)

• font and character set

• header

• paragraph

• line

• tab

Sorting Stacks Given the flexibility of the hypertext approach to data management, it may seem a little out of character to include a sort capability in Transcript. But in two situations, sorting may be important to your stack’s users: flipping through the stack one card at a time and expecting to find the cards in some particular order, and producing reports where the cards appear in some particular order. To accommodate either need, use the Transcript sort command. Although this is one of the easiest commands to use in Transcript, it offers a large array of possible arguments, or parameters. In its simplest form, the sort command looks like this: sort by <expression> Alternatively, you can add some "throw-away" words between the sort command and the by keyword to make your statements more readable. The effect of all these variations is the same; the only difference is in how they read: sort cards by <expression> sort stack by <expression> sort cards of stack by <expression> sort cards of this stack by <expression>

The expression argument must be a text expression. In other words, it must either be text or evaluate to text. This expression can be as complex a field component address as is needed. If each record in our employee database example is stored on a separate card, with the information still in one field called Employee Data, you could sort the stack by employee last name with a command like this: sort by item 2 of field "Employee Data"

Page 231: Software at Speed of Sound -- Dan Shafer

206

Beyond this simple use of sort, there are two other sets of parameters that can be added to the command. The first set concerns the sort direction, and the second concerns the data type of the sort expression.

Selecting the sort direction By default, any sort carried out in Revolution is assumed to be in ascending order. But you can override this assumption with the word descending. Thus, to sort the employees (assuming, again, that they are on separate cards) according to salary, with the highest-paid employee at the top of the list, you would write a command statement like this: sort cards descending numeric by item 6 of field "E mployee Data"

Defining the data type From Transcript's perspective, four different types of data can be sorted: • text (the default and most often used) • numeric • dateTime • international If you don't supply a second parameter, Transcript treats the sort expression as text. This results in a straight ASCII sort sequence.

Numbers don't fall in numeric order in the ASCII sort sequence. If you want Revolution to treat the sort expression as numeric, you must tell it so by supplying the numeric parameter to the sort command.

As you will see in the concluding section of this chapter, Transcript treats date and time data differently from other types of information. This leads to some very powerful applications. But if you want Transcript to treat a particular sort expression as a date or time data type, you must use the dateTime sort parameter.

The international parameter is necessary if you are sorting data that contains special characters called ligatures or umlauts. English has neither of these special types of char-acters, so text is adequate for most English-only applications involving alphanumeric data. However, if your application should be usable by people whose language is not English, you will find this sort parameter quite useful.

Naming the sort field In most cases, you will either supply the name of a field explicitly or program a container name with the name of the field in it. It is possible to sort by the contents of a card field but that is not often done since fields that appear on multiple cards are almost always designed as group fields in a group that is acting like a background. You must be careful about one thing in naming the field by which to sort. If you supply a variable that has not been initialized or if you use a string with no field identifier preceding it, Transcript appears to complete the sort but the order of the cards isn't changed. For example, the line

Page 232: Software at Speed of Sound -- Dan Shafer

207

sort by "Date" appears to work but does not. You must explicitly tell Transcript that the word Date refers to a field.

Confining the sort to marked cards You can instruct Revolution to sort only those cards that have been previously marked. The basic syntax for this form of the sort command is as follows: sort marked cards [of stack] by <container> by [<so rt criteria>]

Sorting by function In addition to being able to sort stacks and fields by their explicit contents, you can also use Transcript and user-defined functions as sort criteria. For example, you can move all of the marked cards in a stack together by the statement: sort by (marked of this card) You might even define a function that handles a sort in a non-standard way (for example, skipping some cards based on contents of fields that are not involved in the sort) and then use a statement like the one below to use that function to sort the stack: sort by mySortFunction

Some complex sorts The following examples are self-explanatory and straightforward, but they show how the sort command can become quite complex and specific: sort dateTime by field "Hire Date" sort descending numeric by item 3 of card field "Ta ble 3" sort descending dateTime by chars 1 to 5 of line 4 - i of field 1

Sorting within fields You can sort information contained within a single field, keeping lines and items intact in the process. You can sort all of the contents of a field by line or by item, or you can select specific groups of lines or items to sort. To sort all of the lines in a particular container, you use a syntax like this: sort lines of <container> by <parameters> where the parameters are the same as for sorting cards (i.e., type of sort and direction) and have the same defaults (i.e., text and ascending). Similarly, to sort a container's items, you would use a command with this syntax: sort items of <container>by <parameters> If you want to sort only part of a container, you can specify that as well. For example, to sort only the first 10 lines of a field, you could use a command like this: sort line 1 to 10 of cd field "Scores" You can use a similar syntax to sort only specified items of a field. If you don't specify either lines or items but you tell Transcript to sort a container, it will sort the lines in that container.

Hypertext Techniques From its inception, Revolution was designed to permit hypertext-style applications to be constructed easily by non-programming end users. We have already seen how the textStyle called link enables you to treat a multiword string as if it were one word. Now

Page 233: Software at Speed of Sound -- Dan Shafer

208

we're going to see how to take advantage of that fact in combination with three functions — the clickChunk, the clickLine, and the clickText — to create extremely usable hypertext-style interfaces and interactions.

Laboratory: Hypertext experiment We'll conduct a brief experiment that will enable you to see how flexible and useful hypertext connections can be in Revolution. For the purposes of this experiment, you can create a new stack with a single card and a single field or add a card with a field to your existing Laboratory stack. With that done, follow these steps to examine the capabilities of hypertext features in Revolution: 1. Type the following text into the field, with a carriage return at the end of the string “note”. The world will little note nor long remember what we say here.

This creates a two-line entry in the field. Use the Font and Style menus to set up the text to be whatever size and font you wish. 2. Select the words "what we say here" with your mouse. 3. From the Text menu, choose "Link". 4. Lock the text in the field. 5. Open the field's script-editing window in one of the usual ways and enter the following script: on mouseUp put "clickChunk = " & the clickChunk wait until the mouseClick put "clickLine = " & the clickLine wait until the mouseClick put "clickText = " & the clickText wait until the mouseClick end mouseUp 6. Click on the first word in the field, "The." The Message window will show, in succession, the following results, waiting after displaying each one until you click the mouse: clickChunk = char 1 to 3 of card field 1 clickLine = line 1 of card field 1 clickText = The 7. Now click on any of the words in the run of text to which you applied the "Link" style at Steps 2 and 3. In the Message box, you'll see the following results; as in Step 6, you must click the mouse after each message is displayed. clickChunk = char 27 to 52 of card field 1 clickLine = line 1 of card field 1 clickText = what we say here 8. Finally, click on the word "nor" in the second line of the field. The Message box results will look like this: clickChunk = char 65 to 67 of card field 1 clickLin e = line 2 of card field 1 clickText = the

Page 234: Software at Speed of Sound -- Dan Shafer

209

As you can see, these new functions allow you to examine the precise contents of the text in a locked field where the user clicks. You can then do a great many things with the results of the click, particularly with the clickText. You could, for example, perform a find operation with the clickText as a parameter to create a hypertext link to occurrences of the word or phrase in the stack. Or you might go to a card named after the text on which the click occurred. These are just two ideas for uses of this information.

Creating Destinations for Hyperlinks In the last Laboratory experiment, I alluded to the idea of creating destination links for linked text. As it turns out, Revolution has defined a property just for that purpose. Called linkText, this property stores information that can be used in responding to the user clicking on linked text.

To see this in action, return to your Laboratory stack. In the Message Box, type:

set the linkText of chars 46 to 61 of field 1 to http://www.runrev.com”

Substitute your own favorite URL if you like.

Now go into the script for field 1 and add this line just before the end mouseUp line:

revGoURL the linkText of the clickChunk

Save the stack and click on the linked text again. As you click the mouse each time, you’ll see the expected messages in the Message Box. As soon as the last line has been displayed, your system’s default Web browser should open to the URL you’ve supplied.

Since we’ve written the line generically, this one routine will result in any link we store in a field acting as a Web-based hyperlink if we attach a URL to it as its linkText.

Date and Time: Special Data Types Let’s now turn our attention to the use of dates and times as special data types. As we just saw, data that is intended to be sorted as if it were a date or time must be pointed out to Transcript as part of the sort command. Other considerations must also be watched when you deal with such data.

Five functions are involved in managing date and time data, and most of them have several versions. The five basic functions are • date • time • seconds • ticks

Page 235: Software at Speed of Sound -- Dan Shafer

210

• convert We will discuss each of these in the order shown.

The date function Before we begin to discuss the date function, we need to understand a little about how Revolution decides how to format date and time information. There are for all practical purposes two formats available: the system format and the default (English) format. By default, Revolution uses the English date formatting, but you can override this behavior by simply including the following line at an appropriate point(s) in your script(s):

set the useSystemDate to true

In its simplest form, the date simply returns the current date in what is called short format. This format is the one you are probably most accustomed to writing, assuming you are in the United States: mm/dd/yy. Thus, January 15, 2005, appears as 1/15/05.

The next simplest form of the command is the abbreviated date. This command can be written out completely or shortened to the abbrev date or even the abbr date. A date retrieved in this form includes the day of the week and the month (both abbreviated) and the year. Neither abbreviation includes a period. January 15, 2005, written in an abbreviated Revolution form is Sat, Jan 15, 2005.

Finally, there is the long date function. This command spells out all of the date's components: Saturday, January 15, 2005.

Usually, you will put the date, or some portion of it, into a field on a card in an openCard or openStack script. There are, of course, many other uses for this function. You can extract the individual items in either the abbreviated or the long date. For example, to pull out the day of the week and put it into a card, you would write something like this:

put item 1 of the long date into field "Day of Week "

If you use the abbreviated date rather than the long date, the abbreviation for the day of the week is retrieved and put where you direct it to be placed. Individual items cannot be extracted from the short form of the date, which exists entirely as one item, unless you change the way Revolution looks for items by changing the itemDelimiter. When you extract such information from a date field, it must then be treated as text and not as a date on which special mathematical operations can be performed.

The time function There are two forms of time functions. The first function, the time, has no qualifiers and returns the current time in hours and minutes. The second is written the long time and adds the seconds to the end of the time. In all other respects, they are identical.

Page 236: Software at Speed of Sound -- Dan Shafer

211

How the time is displayed — for example, as 5:15 P.M. or as 17:15 — is determined by the settings in your Control Panel but this can be altered in Transcript by adjusting the value of the global property twelveHourTime. When set to true, the time shows with the A.M. or P.M. designation; when twelveHourTime is false, the 24-hour clock version (17:15) is used. Individual components of the time functions' return values may not be retrieved.

The seconds function The seconds function can be written as either the seconds or the secs. In either form, it returns the number of seconds that have elapsed since some arbitrary date in the past, depending on the operating system settings.

One useful application for this function is in stacks where you want to time user responses or document usage. Simply store the value of this function when a process begins, and then store its value when the function ends. A simple calculation reveals how much time has elapsed between steps.

The ticks function A tick is 1/60 of a second. You can use the special function the ticks to obtain the current value of this counter. The function has no arguments and returns a number — generally a very large number — that you can use like any other number. Like the seconds functions, the ticks can be used to time the distance between two events. It is 60 times as fine a measurement as the seconds, so it can be used when you need very accurate timing.

Using convert to reformat dates We have seen date and time information in Revolution in six formats: • seconds • long date • short date • abbreviated date • long time • short time Sometimes you will want to convert a date stored in one format into a more readable or usable format. To perform such a task, Transcript includes the convert command. Its syntax looks like this: convert <container> to <format> The container must contain data in a date or time format. The convert command converts this information into the form specified by the format argument. The converted result replaces the contents of the container, so you will usually want to do conversions of dates and times in variables rather than in fields on the screen.

Page 237: Software at Speed of Sound -- Dan Shafer

212

Imagine the consternation of the uninitiated user who sees a nice, readable date such as Jan 15, 1988, suddenly turn into the staggering number 2652048000!

We haven't looked at one of the most useful conversions in Transcript. The dateltems format is a special form of the date that can be used easily in arithmetic. When you convert a date to this format, Transcript returns a comma-separated list of seven items, beginning with the year, continuing down to seconds, and ending with a number indicating the day of the week. The day of the week assigns Sunday as 1, so Monday is 2, Tuesday is 3, and so forth. To see the dateltems conversion work, open the Message window. Put the Message Box into multiline mode by clicking the second icon from the left in its toolbar. Type in these three lines, then press the Enter key to execute them: put "1/15/05" into datel convert datel to dateltems put datel

The Message Box shows “2005,1,15,0,0,0,7.” Notice that because we didn't store a time in the variable date1, Transcript simply substitutes zeros for the time fields. You may be surprised to see that happen if you've worked with more stringent programming languages that return an error if you try to convert something without enough data in the original value. The number 7 at the end of the dateltems list tells us that January 15,2005, is a Saturday.

The real value of the dateltems format is that you can perform math on any item in the list. Want to know the date 45 days from now? Type the following lines into the Message box and see the answer. put “1/15/05” into datel convert datel to dateltems put item 3 of datel + 45 into item 3 of datel convert datel to date date1 -- Message box displays 3/1/05

If you are curious about how this works, go through the same process, but after you perform the addition (the third line of code) and before you restore it to a readable format, type datel into the Message box. Look at the third item. Don't be surprised if it is larger than the number of days in any month! Yet when you perform the conversion back to a readable date format, Transcript takes care of this discrepancy and produces a logical answer.

Trapping the Return and Enter Keys During data entry into a card with more than one field, the experienced user expects to be able to press the Return key or the Enter key to move from one field to another. As we saw in Chapters 6 and 7, Revolution includes two messages you can use to assist the user in meeting those expectations.

Page 238: Software at Speed of Sound -- Dan Shafer

213

The two messages are returnlnField and enterlnField. The first message is sent whenever the user presses the Return key while typing in an editable field. The other is sent in response to the user pressing the Enter key in an editable field. You can write handlers to respond to these messages so that you can customize how your stack responds to these commonly used keys. For example, if you want to simulate a word processor's reaction to the Return key so that Transcript adds a second carriage return and "tab" five spaces, you could write a handler like this: on returnInField put return & return & " " after me select after text of me end returnInField

If you wanted the Return key to be used to move from one field to another, write a handler like this at the card level or higher: on returnInField send tabKey end returnInField

This moves the cursor to the next field and selects all of the text of that field.

Multiple Cards vs. Single Data Source Now that you know how to work with data stored in Revolution stacks, it will be helpful to pause before we look at storing information in text files, to see how two different possible approaches to data-intensive applications in Revolution compare to one another.

The two approaches are quite different from one another.

Multiple-Card Solution For the multi-card solution to a data-centric Revolution application design, you proceed as follows:

1. Create a splash screen main stack.

2. Create a single card substack that has on it the user interface and controls needed to allow the user to navigate through the information being managed.

3. Group all of the interface controls and fields together and assign them the role of a background.

4. Add information to the first card.

5. Create a new card.

6. Add information to the new card.

Page 239: Software at Speed of Sound -- Dan Shafer

214

Each card in the stack, then, represents what is essentially a single record in what might be viewed as a database. As the user navigates through the stack, he or she is actually moving from card to card.

The primary advantage of this approach is that it is all but trivial to build. Very little scripting is necessary. You probably want to build in navigation buttons or links that allow the user to move linearly in the stack, pop back to the first card, go to the last card, and find data in the stack. Printing, without using the Revolution Report Object is not entirely satisfactory, nor is it trivial to implement. But it is possible to print from such a stack, and if printing needs are fairly unsophisticated, this approach works fine. (With the Report Object, even that concern disappears.)

NOTE: As this revision of this eBook goes to press, Quartam Reports has released a new and fairly powerful third-party report generation tool for Revolution. Check it out at http://www.quartam.com.

One-Card Solution Using the one-card solution, you create three stacks: a main stack (splash screen), a user interface stack consisting of a single card, and a data-only stack that need not have any fancy design since the user will never see it.

For the one-card solution to data-centric Revolution application design you proceed as follows:

1. Create a splash screen main stack.

2. Create a single card substack that has on it the user interface and controls needed to allow the user to navigate through the information being managed.

3. Create a stack that stores the contents of the entire database in a single field (separated into items) or, less efficiently, in separate fields where each field holds all of the appropriate contents of all of the records in the database.

4. Through scripting, connect the UI substack and the data substack so that as the user navigates through the data, he or she is simply re-populating the single-card UI stack’s fields with data extracted from the field(s) in the data stack. This will include scripting the addition of new data to the invisible stack through the user interface.

This approach requires substantially more programming than the multi-card approach but has the advantage that it probably requires much less disk storage and memory capacity to run. Even though in both cases all the data is loaded into memory when the application launches (a hard-wired characteristic of Revolution applications), in the one-card solution, the user interface elements are loaded only once. Cards occupy much more space than the data that resides on those cards.

Page 240: Software at Speed of Sound -- Dan Shafer

215

It is worth saying a few words about the issue I raised in step 3 of the one-card solution. I would tend to design such a solution so that there is but one field on the one card in the data stack, with data elements treated as items in a line of text. Since the user will never see this card, your scripts will manage all of the translation between data stored as items and data displayed in the UI card. Using this approach will lend itself much more easily to looping over the contents of a record in the single field and putting that data on the UI card than would be the case if you created separate fields for every item and distributed the data that way. Parsing items is also somewhat faster than parsing individual field contents.

Once you understand the one-card approach to Revolution application data management, you can easily see how you might use an external text file in place of a data stack. That topic is our next focus.

Text File Operations Using an external text file for data storage for your Revolution applications is in most ways quite similar to the one-card solution I described in the previous section. If you use a text file for data storage, you’ll have to take responsibility for loading its contents into your application and for explicitly writing out any changes to the file. None of this is particularly onerous and any difficulty may be offset in your application needs. That’s because external text files have some significant advantages over using Revolution data-only stacks as I suggest in the one-card solution above.

These advantages include:

• partial loading. While a Revolution stack is always memory-resident, you can open and read only part of a text file under Transcript control..

• open on demand. You can open a text file only when it’s needed rather than having it be a sort of integral part of your application as is the case if it’s a Revolution substack.

• universally usable. A text file can be read from and written to by many different kinds of applications (word processors, spreadsheets, report generators, database management systems, and others). This makes the text file far more universally usable in many application situations.

There are two ways you can use text files in your Revolution applications: using a traditional open-read-write-close approach, or using Revolution’s URL approach. In 90% of the cases you’ll encounter, the URL approach will be vastly more efficient. But there are times when only the older-style approach to file usage will suit your needs.

Let’s look first at the traditional file input/output (I/O) mechanisms included in Revolution.

Page 241: Software at Speed of Sound -- Dan Shafer

216

Traditional File I/O

Transcript's vocabulary includes four commands that permit Revolution to work with text-only files using traditional programming techniques. Data can be read from text-only files and used in stacks. Information from stacks can be saved in text-only files for use by word processors, report generators, spelling checkers, and other similar programs. Whether you are reading or writing data, the process is fundamentally quite similar. You use the open file command to open the file so you can put information into it or take information out of it. Then you use the read or write command to perform the actual data transfer. Finally, when you're done, you use close file to end the processing. Let's look at these commands in the order in which we just listed them.

Open file There is nothing mysterious or difficult about the open file command. Supply the name of the file—either explicitly as a string or implicitly in a container — and Transcript establishes a communications channel between your script and the disk file, creating it if it doesn't already exist.

NOTE: The open command does not read the file. In fact, unless you specifically write code to look at the special variable the result, you won’t have any way to be sure the file is open until you actually try to use it. This throws a lot of beginners who expect that when they open the file, the contents will appear somewhere in their stack. You have to go get it explicitly, as explained later in this discussion.

For example, to open a file called Test File, you could simply write this command:

open file "Test File"

Revolution looks in the current default directory unless you specify a path. The default directory is the directory in which your application (or Revolution if you’re in development mode) is located. You can change the default path by setting the defaultFolder property in your script. If Revolution finds a file called Test File where it expects to, it opens it. If not, it creates a new file called Test File in the default directory.

When you open a file, you may optionally specify a mode for the file’s use in your script. To do this, you add the keyword for followed by one of the available mode names:

• update

• read

• write

• append

Page 242: Software at Speed of Sound -- Dan Shafer

217

If you open a file in read-only mode by a command like this:

open file “foo.txt” for read

you will not be allowed to write any new data to that file. Not only that, but if you use “for read” with an open file command, and the file does not exist, Revolution will report an error rather than creating the file for you. The difference between write mode and append mode is that the latter will allow you to add data to the end of the file but not modify any of the file’s existing contents.

NOTE that you can open non-text files in Revolution as well, but since this chapter focuses on managing text, I’m not going into that subject here.

A word about path names The path name of a file is a map that helps your computer locate a file on the disk. To make Revolution look anywhere but the default folder for files you want to open or create, you must supply a path name.

Unfortunately, file paths are one of the larger bugaboos in dealing with cross-platform application deployment. (Note, I did not say they were a big issue in the development of software; they are, but they’re a relatively minor annoyance in that context.) Each of the major operating systems with which we deal -- Macintosh, Unix, and Windows -- has its own way of designating file paths.

On a Classic Macintosh, colons separate volume and folder names from one another. On OS X flavors of the Mac, and on Unix, the forward slash (“/”) does the job. On Windows, a single letter followed by a colon defines the drive (volume) and backward slashes (“\”) are used as directory separators. (Don’t get me started on why this stupid incompatibility exists. It just does.) Revolution does about as much as it can do to help alleviate the situation; in your Transcript code, you can just use forward slashes; as you prepare distributions for Windows or Mac Classic OS, Revolution will handle the translation of the file paths for you.

Path names are either absolute or relative. An absolute path name encodes everything from the volume down to the file. It relies heavily on the volume name never changing, for example. A relative path name causes your application to start looking for the file in the default folder and continuing down the path hierarchy.

Assume we have a document called todo.txt stored inside a folder called My2Do. That folder in turn is found inside another folder called Applications, which is at the top level of the hard disk volume called Driven. Assume that your Revolution application, called My2Do, resides in the same folder. Transcript can access the todo.txt document in an open file command by either of the following two lines: open file "Driven/Applications/My2Do/todo.txt" open file “todo.txt"

Page 243: Software at Speed of Sound -- Dan Shafer

218

The first line above is an absolute path. The second is a relative path. For the most part, you should use only relative paths to files your Revolution applications use. Otherwise, things that are outside your control but affect file access can render your applications unusable. If, e.g., the user renames his or her hard drive, absolute paths become useless. The one exception to this rule arises if you use ask file to get a file location from the user in a system-specific file-open dialog. In that case, you obtain the absolute path and it’s most unlikely to change while the file is open. Since you generally deliver your applications in such a way that they and the data on which they rely are in the same folder, this approach works fine. If you ever need to access information in places like system-standard document folders or preference file locations, you can use some of the more advanced special-file access functions in Revolution for this purpose. I won’t discuss those functions here.

I should also mention in the interest of completeness that the open file command can do more than just open text files for input and update. You can use binary files and you can control the access level (read-only, update, append) granted to your application in using the file.

NOTE: When you write scripts where you use the traditional file access commands to open, read from, write to, and close files, it is a good idea at the beginning of each handler to place the file name into a variable whose name is easier to type than the path to the file. Use put to place the file name in a variable called, e.g., fileToOpen and then use that variable name with other file operations. This is particularly important because Revolution treats file path names as case-sensitive even if the operating system doesn’t.

Reading from a text file The full name of the command that reads data from text files is read from file. It has two alternative forms. The first, which follows, reads all the data from a file until it reaches a defined delimiter character. Generally, two delimiter characters are used in text-only files. Tabs separate individual fields, and carriage returns mark the ends of records: read from file filename until delimiter character The file name must be placed in quotation marks or it must be the name of a variable or container in which the file name has been stored. The delimiter character must be one of Transcript's standard constant characters (tab, return, linefeed, or formFeed) or another character you define using ASCII values or a single-character string. (In case the term ASCII is unfamiliar, it is a standard way of representing characters in computers.) If you supply more than one character as the delimiter, Transcript uses only the first character of this string and will stop reading when it encounters the first instance of that character.

If you want to read the entire file into memory, then you will tell Transcript to read until end, empty, or EOF (an old computer abbreviation meaning End Of File).

Page 244: Software at Speed of Sound -- Dan Shafer

219

There are also forms of the read from file command in which you tell Revolution where to start reading (the number of characters from the beginning or end of the file). You can also tell the command to stop reading after a certain amount of data has been read (useful if you know each “record” in the text file contains a predictable number of characters, for example).

I’ll demonstrate the process of reading from a text file into fields on a Revolution card at the end of this chapter when I provide a small example of a file-based application.

Writing to a text file If you need to save text from a stack to a text-only file so that a word processor, a database manager, or some other program can use it, you will find the write command quite powerful and flexible. Its form is: write < content> to file < filename>

The content that is written to the file can be either the name of a variable, a text string inside quotation marks, or, more commonly, some field identifier. The field name can be either the field's assigned name or its ID number. Locally assigned field numbers are often more useful than names or IDs in write scripts because using numbers permits you to loop through all the fields on a card in a small amount of code. The file name must, as usual, be in quotation marks. The comments I made about locating the file and its path name when I was discussing the read from file command apply equally to the write command. The answer file command (see Chapter 11) will allow the user to name the file to be written, including the path name, using the standard operating system file dialog box.

You must generally insert delimiter characters into your outgoing text. If the program on the receiving end of your output expects a tab-delimited file, add a tab after each field is written to the file. One form of the portion of your script that handles this assignment is: repeat for the number of cards repeat with x=l to the number of fields write field i to file "Test" write tab to file "Test" end repeat go next card end repeat

Closing the File It’s good programming practice to close any file you open in your program. As it happens, Revolution closes any open files when you (or your user) quits the application. Still, it’s nice and tidy to clean up after yourself. The command is, as you’d expect, simplicity itself: close file <filename>

Page 245: Software at Speed of Sound -- Dan Shafer

220

URL-Based File Transactions In most cases when you are using text or other files in your Revolution applications, you will be much better off using the URL method of access than the traditional method we’ve just finished discussing. This approach has some important advantages:

• ease of coding. You don’t explicitly open and close files. You simply use get and put operations to retrieve information from and write information to the files.

• transparency. You need not know in advance whether the file to be used is on the Web or on a local network or on the user’s hard drive; the same protocol approach works for all those situations.

• reality. Using this approach acknowledges that our applications increasingly live in a connected world, where the location of data is not only transparent but almost likely to be outside your machine’s local domain. By using the URL method of access, you get into the habit of accessing the data as if it were remote but doing so in such a way that if the data is local, everything just works.

For example, to open and retrieve the contents of the file in the earlier examples using the URL approach, you’d just code:

get URL “file:todo.txt”

To put the contents of the file into a field, you would just use the put version of the command:

put URL “file:todo.txt” into field “ToDoList”

Assuming that you stored all the To Do items in a single variable called myToDoList inside your Revolution application, updating the file is as simple as:

put myToDoList into URL “file:todo.txt”

As I mentioned earlier, you can’t use this approach to read anything less than the entire file into your application, but you can selectively update portions of the file. For most uses you’ll have for external text files, the URL approach is efficient and effective.

Page 246: Software at Speed of Sound -- Dan Shafer

221

Chapter 11: Dialog Boxes In this chapter, you will learn to use several new Transcript commands

• answer , for dialog boxes requiring only that the user press a button to give your script some information

• ask , for dialog boxes requiring that the user type something to give your script some information

• answer file and ask file , for dialog boxes that help the user find or name external files and stacks

• ask printer , a dialog box from which the user can choose a printer and setup options

• ask password , for a special type of ask dialog box You’ll also learn how to place icons into dialog boxes and how to have Mac OS X display dialogs as sheets within your Revolution application’s windows.

Dialogs and Revolution As anyone who has spent more than five minutes with a computer knows, dialog boxes are a crucial component of designing good applications. Dozens, perhaps hundreds, of kinds of dialog boxes have appeared in computer applications. Some are standard, built-in dialogs (as dialog boxes are often called) such as those you see when you want to open a file (Figure 11-1) or print a document (Figure 11-2). Others are specific to the application, such as OpenOffice’s character-formatting dialog (Figure 11-3).

Page 247: Software at Speed of Sound -- Dan Shafer

222

Figure 11-1. Standard open file dialog

Page 248: Software at Speed of Sound -- Dan Shafer

223

Figure 11-2. Standard print dialog

Figure 11-3. OpenOffice’s character-formatting dialog

Page 249: Software at Speed of Sound -- Dan Shafer

224

Few applications operate without dialogs. But in our exploration of Revolution so far, we have not seen any way to obtain information directly from a user in an interactive way. We learned in Chapter 10 how to read information the user had placed in fields. And we know the Message box can be used as a type of mini-dialog where we display a request for information and read the user's response. But the Message box only works for Transcript commands, so its use is limited. Fortunately, Transcript includes the ability to produce several basic kinds of dialogs. Two have limitations that don't exist in more conventional approaches to dialogs. Nevertheless, they give us a way to ask users for information and all but force them to give us a reply we can use in our scripts.

Three kinds of dialogs We can divide dialog boxes into three basic categories. There are alert dialog boxes, which are generally used for error messages. These frequent screen visitors (see Figure 11-4) usually have an icon that lets us determine the seriousness of the error or message and a single OK button we can click in to remove them from the screen.

Figure 11-4. Typical alert box

Second, we have modal dialog boxes, so named because they put the system into a mode in which only the dialog box responds to user input. Modal dialogs do not have a close box, so users can't simply dismiss them — they must respond to their presence. Alerts are a special category of modal dialogs. Finally, there are modeless dialogs designed to assist the user in some types of processing. These boxes are more like document windows. A modeless dialog generally has a close box, and the user can make it inactive by clicking in another window to make a different window the active one. A common example of a modeless dialog is the Find box in most word processors. If you bring up a modeless dialog and then click in another window, the modeless dialog doesn't get put away; it simply goes behind the newly active window. You can prove this by opening a document window in your favorite word processor and collapsing it so that it occupies only part of the screen. Then call up the Find dialog and click in the document window. You will see that the Find dialog is still on the screen, but it's not the topmost (active) window.

Page 250: Software at Speed of Sound -- Dan Shafer

225

Four modal dialogs in Revolution There are four styles of modal dialogs in Revolution. One, like that shown in Figure 11-5, poses a question to the user and displays up to seven labeled buttons to use as a reply. This is the dialog created using the answer command. The second, like that shown in Figure 11 -6, is generated with the ask command and allows the user to enter text in a small text-editing rectangle before accepting the entry.

Figure 11 -5. An answer dialog

Figure 11-6. An ask dialog

The other two modal dialogs are the standard file dialogs that are part of almost all programs. (Actually, they are part of the operating system, not your programs at all.) The ask file command produces the standard file-saving or file-creating dialog, complete with an optional prompt and an optional default file name. The answer file command produces the standard file-opening dialog, with an optional prompt.

Using Dialogs in Transcript When your script executes an ask, answer, ask file, or answer file command, the user's response is placed into the special It variable. You can then store the response, test it for conditional processing, or do anything else with it that you like. (Remember my frequent cautions about the use of the It variable. Don't keep things in It any longer than necessary.) The answer dialog can also act much like a standard alert, though without the usual icons that give users a visual clue as to the type of warning they are being given.

NOTE: It is possible to replace Revolution’s built-in icons in these dialog boxes, but the technique for doing so is beyond the scope of this book.

Page 251: Software at Speed of Sound -- Dan Shafer

226

The answer Dialog The syntax for the answer dialog looks like this: answer [iconType] prompt [with button1 [or buttons] ] [titled windowTitle] [as sheet]

As you can see, answer requires only one parameter: the question to be posed to the user. If you supply only the question and no possible responses in the form of with parameters, Transcript displays a dialog with only one default response: OK. You can display dialogs from within the Message box. Let's see what a one-answer dialog looks like. Open the Message box and type the following line: answer "This makes no sense!" When you press Return, the dialog shown in Figure 11-7 appears immediately. You can see why this is very much like a standard alert box. It has only one response the user can make, so its job is obviously to inform the user of something, make sure the user has noticed it, and then slip quietly away into the nether reaches of the program.

Figure 11-7. One-answer answer dialog

Some rules about answer dialogs Now let's create an answer dialog with two responses in it. In the Message box, type: answer "This makes more sense!" with "OK" or "Oh ye ah?" The result looks like Figure 11-8. Notice that the OK button is listed first in the answer command line but shows up in the middle of the dialog, not at the right where we expect to find OK buttons. Also notice that the "Oh yeah?" button is highlighted. These two observations come about because of two basic rules in answer dialogs: 1. The options fill in the dialog box as if they were being inserted into the dialog box from the right and pushing existing options to the left. If you put in three of the seven legal options, as we will in a moment, the one listed first in the command line ends up in the first, or leftmost, spot. 2. Revolution always highlights the rightmost button, which is the last one listed. This means this button is the default button that will be activated if the user presses Return or Enter.

Figure 11-8. Two-answer answer dialog

So if you want the dialog in Figure 11 -8 to look like normal dialogs, you have to write: answer "This makes more sense!" with "Oh yeah?" or "OK"

Page 252: Software at Speed of Sound -- Dan Shafer

227

Tailoring text in a single answer dialog The question displayed in an answer dialog is a text field like any other in Revolution. Thus, you can use concatenation to tailor the text in an answer dialog without having to create a separate dialog for each possible event. For example, suppose you need an alert to notify the user that a problem exists in a file. You can simply create a standard format for a dialog and have it add the file name when it is displayed: answer "Error in file " & filename Figure 11-9 shows such a dialog when the file name is Words.

Figure 11-9. Tailored answer dialog

Computer design consideration There is a final point to make about these Revolution dialogs. Most users are accustomed to being able to cancel all but alert dialogs. So it is very important that your script include a Cancel button as one of the options in an answer dialog. (You need not concern yourself with this issue in ask dialogs because Revolution automatically furnishes such a button in those dialogs.)

The ask Dialog Aside from the fact that users type an answer in response to an ask dialog rather than press a button to select a reply from among those you've offered, the ask dialog is similar to the answer dialog. In fact, everything we've said about answer dialogs can be applied to ask dialogs except for the issue of how buttons fill in the dialog. There are two predetermined buttons in an ask dialog, and you have no control over what they say or where they are placed. Syntactically, the ask dialog command differs only in that it permits only one optional with argument: ask [iconType] question [with defaultResponse ] [ti tled windowTitle] [as sheet]

Generally, you will not use the with option when you write ask dialogs. But if you do supply a with argument, the text it contains becomes the default text in the dialog's text-editing rectangle when the dialog appears. The text you supply is selected, as is the case with standard sysem dialogs of this type, so if the user types any key except Return, the typing replaces the text you supply as the default. Users can also edit the text your script places in the rectangle. First they must click somewhere in the text field to remove the selection. Thereafter, they can use any standard editing technique to modify the text. As usual, pressing the Return key or the OK button accepts the text, storing it in the It container. From there, you can process the text.

Page 253: Software at Speed of Sound -- Dan Shafer

228

When you use an ask dialog, you should check the result . If the user cancels out of the dialog, It will be empty and the result will return “Cancel” (or whatever you labeled the Cancel button).

Example of the ask command Open the Message box and type the following line. The resulting dialog looks like Figure 11-10. ask "Which software makes the best stuff?" with "Re volution!"

Figure 11-10. Typical ask dialog

Standard file dialogs in Revolution To create a standard file-saving or file-creating dialog in Transcript, you will use the ask file command. Its syntax is: ask file "<prompt>" [with <defaultName>]

The user's response is stored in the special global variable It. You can then use this response to open, write information to, and close the designated file. Here is a small example of the use of the ask file command:

on closeCard ask file "Where should I save the text?" with "Tem p" if it is empty then exit closeCard open file it write field "Data" to file it close file it end closeCard The user's response — and therefore the variable It — will be empty if the user clicks the Cancel button in the file dialog. To open an existing file via a standard file-opening dialog, use the answer file command. Its syntax is: answer file prompt [with defaultPath] [{with filter | of type} types] \ [titled windowTitle] [as sheet]

The prompt is any string. If you embed HTML formatting characters in the string, you can format it within Revolution constraints any way you wish. If you wish to direct your

Page 254: Software at Speed of Sound -- Dan Shafer

229

application to look in a specific folder for the file, supply a with argument followed by the path in quotation marks.

If you supply a with filter (or of type) parameter, you can ensure that the file dialog only shows files that match the filter list. On the Macintosh (Classic and OS X), you can furnish up to four file types, using their 4-character names. The types are provided in a string in which all of the file types are squeezed together. To show a list of all PICT and TEXT files, for example, you’d use: with filter “TEXTPICT”

Windows-based applications allow you to supply your filters in a slightly more complex form. The filter consists of an optional description text, followed by a comma and then one or more file extension specifications of the form *.ext, where “ext” can be any known three-character file format extension. Various file extension formats are separated by semicolons.

For example, you can pass the following filter in an answer file command where you know you are dealing with the Windows filing system to let the user pick files that match any of several graphic formats:

with filter “Graphics Files, *.jpeg; *.jpg; *.gif”

You can use the of type variation of file filtering, but you should know it is only supported on the Macintosh, so it’s not necessarily a wise thing to do unless you are certain your program will never be run on any other platform.

The ask password variation There are two differences between ask and ask password. The first lies in what happens to the user's answer. Before Revolution places the response into It, the program converts the reply to an integer value and then encrypts it. This encrypted integer can then be compared with a previously stored encrypted integer to see if the person has used the proper password to gain access to the script or some portion of it. The second difference between the plain ask command and the ask password command lies in the fact that the user's answer to an ask password command is not displayed in readable text. Instead, an asterisk takes the place of each character the user types. This prevents people who may be watching the user enter a private password from seeing what that password is. For example, suppose that the first time a new user runs a script, the user is asked for a password of his or her own creation. After the user enters the password, you store the encrypted integer Revolution places in It in a card field called Protect. The next time the same person uses the stack and reaches the password point, he or she enters a password. Your script then simply examines the password value entered this time against the one stored previously. If they are equal, all is fine and the user can proceed. If not, you may want to give the user more chances to enter the correct password or take some other action.

Andrew
Highlight
Andrew
Highlight
Page 255: Software at Speed of Sound -- Dan Shafer

230

You can use the ask password command less securely but more easily by using the ask password clear variation of the dialog. Using this variation, the user’s entry is still obscured by being replaced by asterisks in the dialog, but the text the user enters is not encrypted. You can test the entry against a plain-text value and determine how to proceed.

Setting up a Printer You can use the answer printer command to display a system-generated dialog box through which the user sets up parameters for printing. The command takes no arguments and returns no values.

Displaying Dialogs as Sheets On Macintosh OS X systems, user interface protocol calls for the display of modal dialogs as sheets that appear from the top of the application’s window, accompanied by a visual effect, and are attached to that window until they are answered. To accomplish this in Revolution, simply add as sheet to the end of any appropriate ask or answer command.

This command is ignored on all systems except Mac OS X, so it is safe to use it; Windows, Unix, and Classic Macintosh will display the dialog as expected.

Displaying an Icon in a Dialog Dialog boxes, in particular those modal dialogs that are intended to act as alerts to the user that something has gone wrong or needs their intervention, often have icons in them to enable the user to determine how serious the problems they signal are. In Revolution, you can specify an icon by supplying one of the following names immediately after the word answer . (These also work with ask dialogs but their use there is less common.)

• information

• question

• error

• warning

Note that on some systems, the information and question options produce the same icon.

Page 256: Software at Speed of Sound -- Dan Shafer

231

Chapter 12: Managing Menus and Preparing for Distribution In this chapter you’ll learn

• a good strategy for scripting user interaction through menus

• how to attach scripts to menu items

• the platform-specific issues involving the use of menus

• basic steps to preparing a Revolution stack for distribution as a standalone application

Menu Scripting Strategy When you are working with the menus in your Revolution application, I suggest you not use full-blown scripts to carry out your menus’ actions. Rather, define function calls that will handle the menu interaction and then use one-line calls to those functions in the menu scripts themselves. There are a couple of good reasons for approaching things this way even though it may seem indirect and inefficient.

First, menubar objects tend to be among the most reusable components of Revolution applications. Significant portions of a menubar in one application will be nearly identical in another application. But the specific functions being performed by each menu item may differ. For example, a Print menu item on a File menu might require different preparation in one application than it does in another. If you’ve coded all of the first application’s print-related behavior directly into the menu item’s scripting in the menubar object, reusing the menubar code becomes problematic. But if you’ve only inserted a call to a function called, for example, doFileMenuPrint , then you can keep the entire menubar structure -- including things like shortcut keys and contextual activation or deactivation -- intact and just write or modify a script to handle the menu item selection.

Second, because you want to place the menubar into your application’s mainstack very early in the development process (for reasons explained in Chapter 3), using one-line function calls postpones the necessity of writing all the code you’ll eventually need to place into these handlers. You can effectively use what traditional programmers call “stubs” -- code that calls functions that are minimally implemented. When I’m building an application, I often have a function that looks something like this:

on doFileMenuPrint answer “Print menu called” end doFileMenuPrint

As you develop and debug your code, these “stubs” can be fleshed out. Meanwhile, you can quickly and easily track down times when the code is being called erroneously.

Page 257: Software at Speed of Sound -- Dan Shafer

232

The only exception I make to this rule is cases where one line of Transcript code will carry out the desired menu item function. For example, the copy command is a one-line Transcript command that carries out the Edit menu’s “Copy” command quickly and easily. There’s no need for a stub there.

Scripting Menu Items As we saw in Chapter 3, using the Revolution Menu Builder tool to create your menubar, menus and menu items is quite easy and straight-forward. To make the menu items actually do something when the user selects them, however, you must understand both the menu message dispatching technique and how to write function calls to be made when the user selects a menu item. Then, of course, you can apply all you’ve learned about scripting in this book to create menu item message handlers that do whatever needs to be done.

In this section, we’ll look at each of these phases of menu scripting in turn.

What Happens When the User Selects a Menu Item Menus are implemented in Revolution as groups of buttons. Whenever the user clicks on a button acting as a menu item, Revolution generates the menuPick message and passes along the text of the menu item the user selected. It optionally passes along as a parameter the text of the submenu to which the menu item belongs if there is one.

As you know from earlier chapters, you handle a message by creating a handler of the same name. In this case the handler needs to account for at least one and perhaps as many as three incoming parameters. In our sample, we only have to be concerned with the first parameter -- the text of the selected menu item -- so a skeleton of a menuPick message handler would look something like this:

on menuPick pWhich switch pWhich case “Item 1” -- handle this menu item break case “Item 2” -- handle this menu item break end switch end menuPick

There are no mysteries here. We take the first parameter sent by the menuPick message and we use a switch statement to figure out how to respond to the user’s specific menu selection. Inside each element of the switch statement, we deal with a different menu item as a case and we end each case with a break to terminate processing and return to the calling function or to Revolution itself.

There is an on menuPick handler for each menu (which, you’ll recall, are all button groups) in the menubar object so you deal with each menu’s items in a different script.

Page 258: Software at Speed of Sound -- Dan Shafer

233

Let’s examine a quick example. Go back to the stack you created in Chapter 3. If you don’t still have it lying around, you can reload it from the Chapter 3 stack accompanying this eBook. Open the Menu Builder from the Tools Menu, click the “Edit” button in the upper right area of the screen, and select the Menubar object you built in Chapter 3 (if you followed my advice, it’s called Menubar). Your screen should look something like Figure 12-1.

Figure 12-1. Menu Builder Window for Sample Stack

NOTE: I’ve checked the “Set as Menu Bar on Mac OS” checkbox at the top right of this window. This setting determines where the menubar will appear when you build your standalone application. With this checkbox checked, the menubar appears at the top of the user’s display, which is where Macintosh users expect to find menus. But you can leave it unchecked, in which case Revolution will build the standalone so that the menubar you create here will display at the top of your application window as it does on Windows. While that would violate standard Apple Human Interface Guidelines, you may have a good reason for doing so. Revolution doesn’t prevent you from exercising that creativity.

Select the “File”menu and click on the “Auto Script” button in the lower left portion of the window. If you get a dialog box that says the button already has a script, just click “OK.” This shouldn’t happen but if you’ve been messing around in there when I wasn’t watching, it could happen.

You will see a window like Figure 12-2. This is one of the most helpful little aspects of Revolution. It has examined the menu items in the File menu (there’s only one) and has

Page 259: Software at Speed of Sound -- Dan Shafer

234

created the script for its item. Not only that, it has placed a clear comment where your code for handling this menu item should be placed. Pretty cool, eh?

Figure 12-2. Auto Script Window for Quit Menu Item

Click the OK button to dismiss this window, select the Go menu in the menu list of the Menu Builder window and click “Auto Script” again. This time the script is longer (see Figure 12-3) because the menu has four items in it instead of just one. But as you can see the structures are identical.

Figure 12-3. Auto Script Window for Go Menu Items

Page 260: Software at Speed of Sound -- Dan Shafer

235

Select the File menu in the menu list in the Menu Builder window and click on the “Edit Script” button. You should see the by-now-familiar Script Editor window open, with the auto-generated script in place and ready for your scripting touch.

Select the commented line that says, “--Insert script for Quit menu item here” and type over it with the following single line of Transcript code:

quit

Apply the script and close the editing window. This command will cause your application to quit. It does have one tiny drawback. If you open your application in Revolution and choose the Quit menu item from its application’s File menu, Revolution will quit and there won’t be any warning or confirmation set. This problem is easy to fix, but it’s not about menus so I’m not going to cover it here. Rather, I’ll leave it as one of those infamous “exercises for the reader.”

Now, select the “Go” menu and click on the Edit Script button. Replace each commented line with the appropriate Revolution command.

• Replace the Next Note comment with the command go next card .

• Replace the Prev Note comment with the command go prev card .

• Replace the Last Note comment with the command go last card .

• Replace the First Note comment with the command go first card .

The result should look exactly like Figure 12-4.

Figure 12-4. Edited Version of Go Menu Script

Page 261: Software at Speed of Sound -- Dan Shafer

236

Click the “Apply” button, close the editor, close the Menu Builder window, and save your work.

Now suspend the Revolution UI and try the menu options from the Go menu. Add some new notes. Navigate around. Be sure it works correctly. If one or more menu items don’t work, go back to the Menu Builder, open the Menubar object, and edit the menu script to see if you’ve spelled something wrong.

NOTE: One thing that can appear to “go wrong” in testing at this stage is that Revolution’s ask and answer dialog boxes do not work if you suspend the Revolution UI. Neither, by the way, does the put command which will display a message in the Message box. That is also not available in the standalone.

So far, so good. But I haven’t built any menu message handler yet that uses more than a single line call to a built-in function or command. While I would argue that this doesn’t violate the advice with which I began this chapter, but it isn’t helpful in terms of teaching you how to create the menu item scripts in that more reusable way. So let’s make a small modification to our little tutorial stack to demonstrate that basic capability.

Open the script editor for the File menu, either through the Menu Builder or through the Application Browser. Change the line that now reads “quit” to read “doFileQuit.” Click the “Apply” button and close the script. Now we just have to write the doFileQuit handler. This script belongs in the stack since the menubar is a group that appears on every page in the stack.

Open the stack’s script editor and enter the following handler:

on doFileQuit answer “Save changes?” with “Cancel” or “Don’t Sav e” or “Save” if it is “Cancel” then close this stack exit to top -- or “exit doFileQuit” does the same thing less generically end if if it is “Save” then save this stack close this stack end if end doFileQuit

Apply the script, save your work, and test it.

This is clearly not what you’d do in a commercial stack. You’d want to be sure not to ask your user whether to save changes unless you were keeping track of whether the stack

Page 262: Software at Speed of Sound -- Dan Shafer

237

had changed during this run. But my purpose here isn’t to show you how to create commercial software; rather, it’s to teach you how to script menu items.

Platform Differences The menubar is one of a very small number of places where Revolution’s ability to help you with cross-platform portability runs up against the wall of operating system differences that just won’t go away. If you plan to distribute your application on more than one platform, you’ll want to be aware of these differences.

First, the Macintosh uses the term “Quit” for the menu item by which the user leaves your program, while Windows and Unix uses the term “Exit.” (This is another example, of which there are, frankly, far too many, of the old adage, “The nice thing about standards in computing is that you can have so many of them.” If Apple and Microsoft can’t agree what to call the menu item for leaving an application, what hope do we mere mortals have of every enjoying anything faintly resembling cross-platform compatibility? Oh, wait. That’s why we have Revolution to begin with! Aha!)

There is a fairly easy way to deal with this nomenclature difference. Rather than spoon-feeding it to you (we are, after all, at the end of the first volume of your Revolution learning experience), I’m just going to point you in the direction of the solution. Check out Richard Gaskin’s excellent Independent Study Tutorial that ships with Revolution. Open its menubar and check out the script of the menubar object itself. You can copy-and-paste the entire on mouseDown handler, or a selected portion of it, and your program will behave correctly on all platforms. This is called code-stealing and is a time-honored tradition in computing. Just be sure that the code you “steal” isn’t copyrighted or, if it is, that you get permission. In all cases, appropriate credit to the original author is always a nice courtesy.

Second, you might wonder what happens to the Quit and About menu items when you deploy your application on the Macintosh, which puts these items in different places under OS X than it did under Classic OS. Not to worry. Revolution makes sure these menu items end up in the right place, helpfully moving the About item from your Help menu to the first item under the application’s named menu, and depositing the “Quit” option at the end of that same menu.

Actually, that’s a slight oversimplification. Revolution takes the last item in your Help menu -- whatever you call it -- and puts it at the top of the application menu.

Basic Steps to Preparing Standalones There are many considerations to be taken into account when you are ready to turn your nifty little Revolution creations loose on the world in the form of standalone executable programs. This discussion can only give you the basics so you can get through the process of building a basic distribution. Fortunately, Revolution has made this process pretty easy, so it won’t take us a long time to work through the essentials.

Page 263: Software at Speed of Sound -- Dan Shafer

238

Creating a basic, fairly generic-looking standalone executable version of your Revolution stack (application) involves just three basic steps:

1. Set your standalone application’s parameters by choosing “Standalone Application Settings...” from the File Menu

2. Decide the type of distribution you want to build and the target platform(s)

3. Ensure Revolution has all the stacks set to include

4. Define a few parameters that affect how your application behaves

5. Save the stack with the standalone settings in place

6. From the File Menu, choose “Save As Standalone Application...”

Beyond these steps you can do many things such as identify icons with your application on the user’s desktop, provide program information that the user can examine from the Finder or Explorer or desktop, and a dozen or more other such niceties. I’ll ignore those for now.

Choosing Your Standalone Settings Go to the File menu and choose “Standalone Application Settings...” A screen something like the one in Figure 12-5 will appear.

Page 264: Software at Speed of Sound -- Dan Shafer

239

Figure 12-5. Opening Screen of Standalone Application Settings Process

No Need to Do Much

Let’s start by pointing out that if you are just building a standalone application for testing or internal use and you don’t really care much about details -- particular such application “finish” niceties as custom icons, proper version identification, memory allocation on Macintosh Classic and other such details -- you can get quickly to a standalone executable simply by:

• Selecting the platform(s) for which you want to create standalone(s)

• Closing the settings window, saving when prompted

• Choosing “Save Standalone Application” from the File menu

In fact, when I’m just running an app through the standalone creation process for testing purposes, that’s often when I do.

Page 265: Software at Speed of Sound -- Dan Shafer

240

However, if you are building an app for distribution, you undoubtedly will want to pay attention to some of the details that are under your control in the Standalone Settings dialog. The rest of the chapter is devoted to discussing the highlights of each of the tabs in this dialog to give you a basic idea of the kinds of things you may wish to control for your applications. A thorough explanation of each option is beyond the scope of this book but is the subject of an upcoming eBooklet from Shafer Media.

General Settings In the General tab of the Standalone Application Settings dialog, you can set up three basic parameters for your application: name, how the Revolution compiler should deal with required inclusions, and property profiles. I’m going to discuss the first two but leave a discussion of property profiles for a subsequent eBooklet.

Name Your application’s name may or may not be the same as your stack file name, which is the default used here. You can make this any legal name you’d like, keeping in mind naming rules and conventions for the target platforms for which you are creating standalone versions. Note the instruction not to include file extensions that indicate a standalone; the standalone builder will handle this for you.

Inclusions Very often when creating non-trivial applications with Revolution you’ll use commands that rely on a library that is not part of the core functionality of Revolution. Or your application may depend on a database to operate. In such cases, you need to make sure that all of the necessary script libraries and other support files are included in your standalone as it is built. There are two ways to do this.

By default, the “Search for required inclusions when saving the standalone application” radio button is on. This has the effect of allowing Revolution to look at your code, figure out what stack libraries and database support it needs and automatically load them into your standalone during the compilation process. In almost all cases, this setting is just fine. However, you may want to take more direct control of this process for any of a number of reasons (including some possible errors in locating and loading appropriate libraries that have been reported in Version 2.6 of Revolution).

In that case, click on the “Select inclusions for the standalone application” radio button and then select the libraries and other supporting cast members you know you need to include in your application. For example, if you’ve create custom ask and answer dialogs, you don’t need to load those defined by Revolution so you can uncheck those checkboxes. If you don’t use custom cursors, you probably don’t need to check the “Cursors” checkbox.

You probably know your application well enough to know which, if any, of the script libraries you need to include with your program. If you leave out one that your program

Page 266: Software at Speed of Sound -- Dan Shafer

241

depends on, the program will not function correctly and the user may not have a clue why.

In general, your applications will rely on one database if it uses any at all. If you do use a database for your application, you’ll want to check the “Database Support:” checkbox and then select the database you need it to support.

Stacks Settings The next tab in the Standalone Application Settings dialog is labeled “Stacks”. By default, this window (see Figure 12-6) will list all of the stacks in your application’s stackfile (i.e., the mainstack and all substacks).

Figure 12-6. Stacks Settings Tab

In Figure 12-6, the application consists of two stacks, the main stack for the application and an “about” stack. The stack file is listed in the large field to the left of the window; the individual stacks comprising that stack file are listed in the lower right area. Notice that there are buttons that allow you to add and remove stack files.

Page 267: Software at Speed of Sound -- Dan Shafer

242

Probably 99% of your applications will consist of one mainstack (which is often what is called a “splasher” that loads at launch, sets things up and then loads substacks as needed) and one or more substacks. These substacks can be intended to hold data. If they are, then you need to check the “Move substacks into individual stackfiles” checkbox. For now, ignore the two checkboxes below that option; they have potential uses but are beyond the scope of basic settings. By checking this checkbox you tell Revolution that you need these substacks to be set up so that their contents can be changed when the application is running. Remember that Revolution builds your application so that the mainstack contents cannot be altered at runtime. That is in keeping with the general rule that programs don’t modify themselves while they are running. If you don’t separate out the substacks where you intend to store data, they will also not be able to be updated when the program is running.

You can safely ignore the rest of this tab.

Copy Files Setting The Copy Files tab of this settings dialog (see Figure 12-7) is one you probably won’t use a lot. But if you have non-stack files that are an integral part of your application -- for example, a text-based license file -- you would add it here so that Revolution will compile it into the application when it creates the standalone.

Figure 12-7. Copy Files Setting Tab

Application-Specific Settings The next four tabs in the Standalone Application Settings dialog deal in turn with each of the platforms for which you can create a standalone product. These platforms are:

• Mac Classic (aka Mac OS or OS 9)

• Macintosh OS X

Page 268: Software at Speed of Sound -- Dan Shafer

243

• Windows (all 32-bit flavors)

• Unix (a number of variants)

When you click on one of these tabs the first time after installing Revolution, the system checks to see if you have downloaded the appropriate engine for the target operating system. If you haven’t, you’ll see a dialog like the one shown in Figure 12-8.

Figure 12-8. Dialog Inquiring About Missing Platform Engine

If your license permits you to construct standalones for that platform and you want to build this application for that target, click “Download”. You will not have to download this engine again. If you click “Cancel” at this point, you will not be able to build an application targeted at the platform in question.

Each of the platforms has its own set of configuration options that you can control as part of creating a standalone application for that platform. Figure 12-9 shows the tab for Macintosh OS X. The other windows allow similar but different options. I’m not going to go into the individual platform settings in this book.

Page 269: Software at Speed of Sound -- Dan Shafer

244

Figure 12-9. Mac OS X Platform Target Settings Tab

As you can see, this dialog allows you to pick icons, set up operating system-specific parameters, versioning and information strings and similar items that the OS X system would expect or at least know what to do with.

One noteworthy entry in all of the target-platform settings tabs is the one labeled “Icons to display on ask and answer dialogs.” By default, when a standard system dialog appears in a Revolution application, its icon is the Revolution “R” with a smaller icon indicating the type of dialog (see Figure 12-10).

Page 270: Software at Speed of Sound -- Dan Shafer

245

Figure 12-10. Standard Default Dialog Icon

If you define the type of dialog in the ask command, as in: ask information “Whassup?” with “Not Much”

you get the standard “information” icon but, as you can see in Figure 12-11, you also get a small Revolution “R” in the icon area. (This is true only in OS X; in Windows, the “R” icon is not included in dialogs for some reason.)

Figure 12-11. Standard Information Dialog With Revolution “R”

You can customize these standard dialogs by choosing a specific icon to replace the “R” in your programs. Clicking on the magic wand looking icon next to either of those fields in the platform settings tab brings up a dialog (see Figure 12-12) where you can pick an icon to replace the “R”. The “small application icon” is the one that gets used in this case.

Page 271: Software at Speed of Sound -- Dan Shafer

246

Figure 12-12. Standard Icon Selection Dialog

(There are other icons selectable from the dropdown menu at the top of this tab.)

Determining How Your Users Report Bugs The final tab we’ll look at in the Standalone Application Settings dialog is the one labeled “Bug Reports.” Its tab contents are shown in Figure 12-13, where I’ve enabled the contents by checking the “Include Error Reporting Dialog” checkbox which is off by default.

Of course, if you write bug-free code, you can skip this setting. In fact, many application developers don’t include this dialog, preferring instead to have their customers call them with bug reports. But if you’d like your users to be able to file bug reports about your programs, this is a way to make it relatively easy. The fields are pretty self-explanatory.

Page 272: Software at Speed of Sound -- Dan Shafer

247

Figure 12-13. Bug Reports Settings Tab

The End...For Now Well, this brings us to the end of the main text of Volume 1 of this three-volume series on scripting in Revolution’s Transcript language.

The next “chapter” of this book is really a detailed walk-through of the process of designing, building, programming, and deploying a reasonably complete stand-alone application. It wraps up all that I’ve been talking about this book and serves as both a review and a chance to build something more interesting than the toy applications I had to confine myself to as I wrote the book.

Vive la revolution!

Page 273: Software at Speed of Sound -- Dan Shafer

248

Afterword (or on postReadBook ):

Building a Real-World Application At the Speed of T hought

I live much of my life based on a constantly changing To Do List. Over the years, I’ve learned that I not only am not alone in that, but that a great many people use such lists to manage their busy lives.

I’ve spent a lot of time looking at various To Do List manager applications, but none of them quite met my own personal needs and expectations. In fact, I find that I’m frequently looking for something just a little different from what the folks who design and build software for mass consumption think I want. I’m sure this has more to do with my peculiarities than with any failing on their part, but it is no less real an issue for that. This is perhaps the primary reason I have gravitated over the years to tools like Revolution’s predecessors, HyperCard and PLUS.

In this chapter, as I’ve been promising throughout the book, I’m going to build a To Do List manager that does things my way. And because you’ll have access to the source code and widgets that I use to build it, you’ll be able to take it apart, put it back together your way, and thus begin to realize, perhaps for the first time, how incredibly powerful it is to be able to create software for yourself that solves your problems the way you want them solved.

I’m going to start with a product design, a description of what I want this new application, which I’m calling ToDoPlus, to do and basically how I see it behaving. Then I’m going to go through the process of building it in a series of steps that will include:

1. Designing and building the menu system

2. Locating, studying, and figuring out how to incorporate a calendar object into the design

3. Figuring out the text file format for saving ToDoPlus lists and items

4. Laying out the screen in its entirety (creating the user interface)

5. Creating the ToDoPlus file and getting it to open at launch or create itself if it doesn’t exist

6. Program the functionality of the buttons

7. Program the menu functionality

Along the way here, I’ll point back to places in the book where I’ve discussed in detail how to do some of these things so I won’t have to be painstakingly instructional in this discussion. Hopefully, this will enable this exercise to act not only as an interesting undertaking in its own right but also as a reminder of what you’ve learned in this book.

Page 274: Software at Speed of Sound -- Dan Shafer

249

ToDoPlus Design Any good program that has any sort of complexity at all associated with it needs to begin with a clear design document. In the world of software development where you are frequently working for clients who are not all that clear about what they want or need (but will be lightning quick to tell you precisely what they don’t like), design is essential. But even when you’re building something for your own use, it’s a good idea to begin with at least a solid outline and clear view of what you’re creating.

I want ToDoPlus to enable me to maintain a single list of tasks, where each task has:

• a description

• a priority

• a status

• a start date

• a due date

• optional note

To make the user interface clean and elegant, I want to be able to select start dates and due dates for items using a calendar interface rather than typing in dates by hand because that will make the whole process less error-prone. (Besides, I enjoy being a difficult client even when I’m also on the other side of the desk; kind of masochistic, isn’t it?)

Once I’ve created a list of To Do items, I want to be able to, as they say in the software business, “slice and dice” it any number of ways. Specifically, I want to be able to sort the items in the To Do list by:

• date due

• priority

• status

I want to be able to focus my view of my To Do list so that I can select out individual items and effectively hide them based on:

• whether they are currently due, due in the future, or past due

• the lowest-priority items that I want included in my view

• whether the items are pending, completed, or on hold

By default, I want to see all of the items in my To Do List, sorted by priority, with top-level “A” priority items sorting to the top of the list.

Page 275: Software at Speed of Sound -- Dan Shafer

250

Finally, I’ll need the ability to create a new task, edit an existing one, or delete a task regardless of whether it’s been completed or not. I also want to be able to attach a note to a To Do list item at any time and I want to be able to mark tasks completed, leaving them on file so I can later see all that I’ve accomplished. I want to be able to click on a task in a scrolling field and have its details show up in the same window but in a different place so I can edit it.

I’m deliberately leaving out some things included in many To Do List applications I’ve seen because I just don’t need them. For example, I’m leaving out the ability to search for a task; this would be pretty easy to do in Revolution and Transcript, but I never find myself needing to do this. If you do, I’ll leave adding that feature as an exercise for the reader. I’m also confining the application to supporting one user with one set of tasks and I’m ignoring the notion of projects or categories. I always need to keep my To Do list for everything I’m working on, so having tasks connected to projects is a layer of complexity I neither need nor appreciate.

Building the Menu System If you’re like me, the notion of beginning to build an application by starting with the menus may seem a little...well...odd to say the least. Very often, in fact, you may be accustomed to tossing menus in as all but an afterthought.

But in Revolution, it is really, really important to build the menus first. You don’t need to connect them up to anything and you can always go back later and modify them, so it’s not essential that you get them exactly right. But as we saw in Chapter 12, you must allow for their presence at the outset of building your program or you can encounter a reasonably unpleasant mess later when you try to jam them into a partially finished project.

After giving the matter a lot of thought, I’ve decided to create a menu system that creates four menus, each of which has several menu items, none of which is a cascading submenu.

The menus are:

• File

• Edit

• ToDo

• Help

On the File menu, I’ll have only a Quit item. I never need to open the file containing my tasks because the application does that automatically when it opens. Saving the file takes palce automatically each time it’s updated by the program. And I’m not handling printing in this version of the application because I haven’t yet taught you enough about printing to explain it.

Page 276: Software at Speed of Sound -- Dan Shafer

251

The Edit menu has the standard cut, copy, paste, and clear items.

The ToDo menu is specific to this application. It enables me to create a new task, delete a selected task, mark the currently selected task as being complete, and add a note to the currently selected task. As it turns out, I will also have buttons in the UI for these purposes and I’ll tend to use them almost all the time. But it’s generally considered bad UI design to allow the user only one way of accomplishing something and even though I’m the only user, I am not inclined to fly completely in the face of UI design guidelines.

Finally, the Help menu will have an About ToDoPlus menu item and a Help menu item.

Creating the Menus From the Tools menu, open the Menu Builder. In the opening window, click on the “New...” button in the menu area on the left side. A dialog box appears. Name the menubar “Menubar” (see Figure A-1). Notice that it already includes menus called File, Edit, and Help before we even start.

Figure A-1. Creating a New Menu Bar Named “Menubar”

When you click the “OK” button, Revolution opens the menu editor as shown in Figure A-2. This is where most of the work of building menus and menu items takes place.

Page 277: Software at Speed of Sound -- Dan Shafer

252

Figure A-2. Menu Bar Editor

The next two things I generally do at this point are to check the checkboxes labeled “Set as Menu Bar on Mac OS” and “Preview in Menu Bar.” If you’re planning on distributing your finished application on Macintosh systems, you’ll certainly want to do the former. The latter is a convenience that allows you to watch the menubar as it’s building and I just find it comforting. It’s not essential that you check that one. In fact, I toggle that one on and off during development as the mood strikes me!

Working With the Pre-Built Menus The File menu already does what I need it to do: it supplies a Quit mechanism. I’ll have to hook it up later to do what I want to do when I quit the application, but I don’t need to touch this menu right now.

Page 278: Software at Speed of Sound -- Dan Shafer

253

I have two other menus that Revolution has generously provided. I’m going to finish them off next and then add my custom menu.

The Edit menu is a piece of cake. It is fine the way it is. (I just have to find an easier job than this!)

The Help menu is also fine as it is, except that I’d like the “About” menu item to have a less generic name, so I rename it “About ToDoPlus.”

That’s all there is to creating the standard menus in a Revolution application. Now there may be some ways that could be made easier, but I’m hard-pressed to think of any off the top of my head. Just another reason I love working in this tool.

Creating the ToDo Menu and Its Items Now it’s time for me to create my first custom menu, the ToDo menu. As you’ll see, it’s not only all but trivial to do so but Revolution actually saves me a bunch of typing with one of the cooler features of any programming tool I’ve used.

To start the creation process, I click on the “Edit” menu in the list of menus in the menubar and then click on the “New Menu” button. Revolution inserts a new menu after the Edit menu. I rename it “ToDo.”

Near the bottom of the area where I’ve defined this menu, I give it the mnemonic “T” and then shift over to the right pane to add its menu items.

I click on the “New Item” button and a new menu item appears in the menu item area. I change its name to “Create Task.” Now, in the area beneath where I’m defining menu items, I click on the checkbox next to the word Mnemonic. Then I click on the popup menu and select the letter “T” from that popup. This creates a shortcut that I could use on Windows (if I ever used Windows) to access the menu item. Next, I click on the checkbox at the bottom of the window labeled “Shortcut” and I type in the letter “T” there. This supports Command-key shortcuts on Macintosh.

Table A-2 describes how I assign mnemonics and keyboard shortcuts to the other menu items on the ToDo menu.

Table A-2. Mnemonics and Keyboard Shortcuts for ToDo Menu Items

Menu Item Mnemonic Keyboard Shortcut

Create Task T T

Delete Task D D

Mark Task Complete M M

Add Note to Task N N

Page 279: Software at Speed of Sound -- Dan Shafer

254

A couple of things are noteworthy in Table A-2.

First, notice that I didn’t use “C” as the mnemonic or keyboard shortcut for the “Create Task” item. I’m already using “C” as the shortcut for Copy. Now I could use “C” for the mnemonic because the user would have to type Alt-T-C to create a new task so there’s no conflict. But I’m nothing if not consistent, so I opt to go with the same letter for the mnemonics and the shortcuts. Not everyone would agree with me. You don’t have to, either.

Second, notice that I’ve used “N” for the mnemonic and shortcut for adding a note to a task. Normally, this would be taboo because “N” means “New File.” But since I’ve decided at the outset not to allow the user to create multiple files of To Do List items, I freed up the “N” and decided to use it here. Again, not everyone would agree with me. I won’t tell if you don’t.

Time to save my work again.

Generating the Menu Program Code Revolution will generate a skeleton of the code I need to handle user interaction with the menus and menu items. Again, this is one of those huge time-saving features that the Runtime Revolution folks have taken time to bake into the development environment that save gobs of time.

I click on the ToDo menu in the left portion of the Menu Builder window and then click on the “Auto Script” button in the bottom left area. A dialog box like the one shown in Figure A-3 appears.

Page 280: Software at Speed of Sound -- Dan Shafer

255

Figure A-3. Auto Script for ToDo Menu Displayed in Confirming Dialog

This creates what programmers call “stubs”, place-holders for future programming code. When the time comes to make the menus function as we wish, we’ll just edit these scripts either directly in the IDE or through the Menu Builder, and replace the commented lines like “--Insert script for Create Task menu item here” with the actual scripts we want executed. The rest is automagical. Cool, eh?

I do this with all the other menus in my project and then save it again. The menubar is now in place and ready for me to attach scripts to the menu items. We’ll come back to those later because there is almost nothing in my ToDoPlus application that is only carried out via menus. So I’m going to write scripts that can be called from the menus and from the buttons. That will be easier to hook to the menus when I’ve finished the actual code and know what I’ve called the handlers.

I save my work and I’m ready for the next step: figuring out how to incorporate a calendar interface into the application.

Finding and Integrating a Calendar Object One key part of the design for the ToDoPlus application is the use of a calendar layout to facilitate the entry of start and due dates. While I’d be prepared to write such a routine

Page 281: Software at Speed of Sound -- Dan Shafer

256

from scratch, wouldn’t it be nice if some wonderful soul had already done all or most of the work for me?

This brings me to the time-honored tradition in software development called “code shopping.” I plan to see if I can dig up a pre-existing calendar object that at least comes close to doing what I want. If I can, I know I can save a lot of time even if it needs extensive modification.

I start out at the Runtime Revolution site where a lot of user contributions to the Revolution developer community have been accumulating over the past year or so. Here’s the URL to the page where developer contributions are listed and described: http://www.runrev.com/Revolution1/developercentral/usercontributions.html.

Once there, I do a search for the word “calendar” to see what, if anything, pops up.

The first thing I find is something called Date_Picker.rev. A quick read of the description seems to indicate that this has some promise, so I download it, set up its parameters and install it in my stack. But the user interface it presents is a small button that pops up a calendar. Cool idea, but not quite what I’m looking for (though I can use this if I can’t find a better answer).

I continue my search. Next thing I find is Shao Sean’s Calendar Object. The description sounds like it might be exactly what I’m looking for; it even mentions using the object in a PIM (Personal Information Manager) and my ToDoPlus ultimately will be integrated into the PIM I plan to design. So I download it, poke at it a bit, and find that it is exactly what I had in mind.

But I figure I might always find something better, so I continue my search. I run across Sarah Reichelt’s Calendar stack, so I download it and play with it a bit. It seems a tad more responsive than Sean’s, but the interface isn’t automatically set up to be what I want; it uses a date in a locked field like a button to activate the graphical calendar which, when displayed, is pretty cool.

This exhausts the supply of Revolution calendar implementations that are available through the developer download area. More than ample, as it turns out, and Shao Sean’s object clearly fits the bill so that I don’t need to continue my search (though I could do so in a number of ways).

So I open the Calendar Object stack and discover that the calendar interface and script are all contained in a single background object.

I open the group’s script to see how hard it looks like it will be to make the calendar interact with my application. The first thing I’m impressed by is almost three pages of comments that document all the handlers and events and variables involved in the

Page 282: Software at Speed of Sound -- Dan Shafer

257

group. Nice. As I scan that data, I can see that the stack anticipates many developer needs for the integration of a calendar navigation UI, including a lot of stuff I won’t need. But it clearly gives me what I want (in the form of the “calendar_dateClicked” event) and allows the user to navigate around by year and month. That’s good enough for me!

This is very cool because it means I can simply copy and paste the whole background into position in my stack and then write code to handle the calendar_dateClicked event. The result looks like Figure A-4.

Figure A-4. Calendar Interface Placed on ToDoPlus Stack Window

I will still have to figure out how to hook up this object’s script later on, but for right now it’s only necessary that I have the UI component in place and that I know it will allow me to react to the user clicking on dates in the calendar. The rest, as they say, is SMS -- Simple Matter of Software.

Designing the Text File for To Do Items Things are coming along nicely and I’ve only really invested about 20 minutes into the development process so far.

Page 283: Software at Speed of Sound -- Dan Shafer

258

Next up on my design checklist is to figure out how to structure the text file where I’ll store the items on my To Do list. I’ve decided to use a text file rather than a full-blown database (which Revolution would allow me to do) for a couple of reasons.

First, text files are really easy to deal with in Transcript.

Second, I don’t need the detailed structure or relationality that would justify using a database like MySQL.

Third, having information stored in a text file is useful if I want to do things with the data that are either hard or impossible in Revolution. Over the years, I’ve learned that my original ideas for the use to which data might be put are obsolete about 10 minutes after I make an irrevocable decision to store data in some hard-to-manage or difficult-to-export file format.

So the only question is how best to store the data in this text file. This involves making two basic decisions.

First, I have to decide if I want each To Do item to be a single line in the file or whether I want each item to consist of a number of lines, one for each bit of information I’m tracking. Second, I have to figure out what to use as a separator (delimiter) between the data items in either case.

I like the idea of one line being equivalent to one item because I know this will make the retrieval of data from the file in Transcript efficient and will require me to write less code than if I have to traverse a bunch of lines to get there. So I’ll go with the line=record approach. Now what to use for a delimiter?

The important thing about choosing a delimiter is to select a character that isn’t likely to crop up inside any of the individual data elements (description, priority, status, start date, due date, or notes). This eliminates common characters like commas, quotation marks, colons, and the like.

I have almost always chosen to use the vertical “pipe” character (even before I knew it was a Unix thing) and I see no reason to change at this point. So I’m going to set up my file so it looks like this:

order|description|priority|status|start date|end date|note

This will prevent me from using carriage returns in notes. I could accommodate that by having the second (and subsequent) line of the entry contain the notes but I don’t anticipate this will be a problem for what I have in mind and this keeps things nice and simple.

Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Andrew
Highlight
Page 284: Software at Speed of Sound -- Dan Shafer

259

I’m also going to give the file an arbitrary title. Heck, it’s my file, right? And I’ve decided to limit each user of ToDoPlus to a single file, so if I hard-code the name, I’ll save some programming time later. The file will be called “MyToDo.txt.”

What’s This order Thing About? Notice that I’ve added an item I haven’t yet talked about, called “order.” I’m going to use this item slot to keep track of the order in which items stored in the file should be displayed when they are shown. It turns out this will have other uses as I go along, but this one drives the decision. If I decide, e.g., to filter my To Do items so that only pending items are shown, I need a way to detect this. As you’ll see, this becomes an important idea as the program development unfolds but it starts out as a relatively simple idea. So goes software. Sometimes.

I need a way for the application to keep track of which items in my list are displayed in the To Do list field and which aren’t, based on filtering out certain types of tasks. I also need a way for the application to make the connection between an item that’s been selected in the To Do list field for editing in the task management region and the changes being made to that item when it comes time to update the item. This will become much clearer as we go along, but for now, just trust me.

After much thought, experimentation, and a couple of blind alleys, I decided (with some help from other Rev designers) that the right way to approach this problem is to keep track of the display order of items right in theList and to always keep theList sorted by the display order. That way, when the user clicks on, say, the third item in the To Do list field, I don’t have to try to translate that to which task in theList that corresponds to as I’d have to do if theList were not aware of its position in the field.

As you will see, there is a very neat way to keep track of these important connections by using built-in Revolution functions once I’ve decided to store the display order information directly in theList rather than trying to maintain some intermediate variable that would act as a sort of “redirection” link. (Don’t laugh; that was my first design in this case. Turned out to be a real nightmare.)

Laying Out the User Interface The main screen of the ToDoPlus interface will be divided into three regions. In the upper left corner of the screen, I’ll position the calendar and any date-selection-related buttons and components I need. The rest of the upper half of the screen will be devoted to displaying the To Do list and managing its sort order, selection criteria, and display. Across the bottom, I’ll place the area where an individually selected task will appear along with all the UI elements needed to operate on it.

This gives me a nice, clean, separated interface that the user should navigate with relative ease.

Page 285: Software at Speed of Sound -- Dan Shafer

260

Now I’m ready to lay out each of these groups of related UI components.

Creating the Calendar Navigation and Date-Selection Region

I’ve already snagged the calendar object, so the only question is what other user interface elements I’ll want in the same area, relating to date-selection. It seems to me that I’ll want to be able to select a date and then tell ToDoPlus to use that date as the start date. Similarly, I want to click on a date and tell the program the selected date is the ending date. And from experience dealing with calendar “stuff” (sorry...meaningless technical term), I’m pretty sure the need to navigate back to today on the calendar will arise as well.

Might as well use three buttons for these operations and arrange them below the calendar, giving the calendar itself prime navigation space.

Figure A-5 shows you what I ended up with as my first design here. I always try to keep in mind that moving objects around and renaming things is so trivially easy in Revolution that no design is ever more locked-in than wet paint. I love that because as I work with the application I’m liable to change my mind several times about how I want things arranged.

Page 286: Software at Speed of Sound -- Dan Shafer

261

Figure A-5. Calendar and Date Area Laid Out

(The calendar isn’t displaying any dates in Figure A-5 because I haven’t yet hooked it up and told it what to do when it opens. I’ll get to that when I start connecting UI components later in the chapter.)

I notice that the label on the “Set Start Date” button is crowding the margins a bit but I like the way the two buttons side by side exactly match the width of the calendar, so I’m going to let that go, at least for now. If it bugs me later, I can always fix it because (and I never get tired of saying this) I own the freaking software! (Picture malevolent laughter here.)

Creating the To Do List Management Region Now comes the fun part. I have a pretty good idea what I want the upper right portion of the display to do, but laying it out is going to take some thought.

Here’s what I think I need in the way of componentry:

• a scrolling list field to display To Do items

• a control to select which of three criteria to sort the list by

Page 287: Software at Speed of Sound -- Dan Shafer

262

• a set of check-boxes that together determine whether I see current, past-due and/or future tasks in the list

• a set of radio buttons (or a popup menu) to choose the lowest-priority item to be listed

• another set of radio buttons (or a popup menu) to choose the status of tasks to be shown (including a choice of “All Tasks”)

• some means for telling the program whether it should use the selection criteria or display the whole list and ignore the selection critera

This last control or set of controls will enable me to set up a group of criteria for filtering the list and then quickly switch back and forth between that view and a whole-list view.

I’m going to start out by placing the scrolling list field, then laying out the four sets of sort-select controls.

After thinking about it for a bit, I decide that the control that determines and displays the sort order should be separate from those that determine selection criteria because regardless of those criteria, whatever list is shown will be sorted by that attribute. As a starting point, then, I put the sort options into a popup menu at the upper right corner of the scrolling list field.

I add an option menu by going to the Object menu and selecting the New Control menu and then choosing Option menu. I drag it to the upper right corner of the window. Then I open its inspector (by double-clicking, which is my preference for invoking inspectors) and fill it out as shown in Figure A-6.

Page 288: Software at Speed of Sound -- Dan Shafer

263

Figure A-6. Object Inspector Completed for Sort

Notice that I gave it the label “Select...” to give myself a visual reminder that I’m supposed to select something from this menu. It is important that I also make “Select...” one of the menu items in the list near the bottom of the window, too, or Revolution won’t remember the label at runtime and will display instead the first option it finds in the menu items list (which would be Due Date).

Page 289: Software at Speed of Sound -- Dan Shafer

264

I label the button “Sort by:” so I won’t forget in my dotage what it is this button is designed to accomplish for me.

I’m going to arrange the controls that handle the filtering of the To Do list as a series of three vertical lists, arranged horizontally under the scrolling list field. This will make them always visible and allow me to see at a glance what filters I’ve applied to the list. In accordance with good user interface guidelines, I’ll use a set of checkboxes where the user can choose any or all of the time-related criteria to use as a filter: Current, Past Due, or Future. These are not mutually exclusive, so the user can select one, two, or all three of them.

For the other two items, I’m going to use radio button sets.

There’s nothing complicated here; the result is shown in Figure A-7.

Figure A-7. To Do List Management Area of Screen Laid Out

Hang on a second here. This will work fine, but it looks unprofessional. Why? Well, notice the difference between the bottom edge of the calendar area and the bottom edge of the list management area. Yet the entire bottom portion of the screen is devoted to dealing

Page 290: Software at Speed of Sound -- Dan Shafer

265

with selected tasks one at a time, so it’s going to be a self-contained horizontal UI grouping. If we proceed along these lines, we’ll end up with a relatively large blank area above, below, or surrounding the calendar navigation area. That would be uncool. So how to address this problem?

The most obvious solution I can see is to convert the two sets of radio buttons to an option menu. Maybe we can also lay out the checkboxes horizontally, with the two option menus alongside. That seems like it has some promise, so I delete the radio button groups I’ve created and place new option menus in their place. After I shift things around a bit, I’ve got a layout that looks like Figure A-8. Much more symmetrical and compact.

Figure A-8. New and Improved Layout for To Do List Management Area

I used the align tool to get the bottoms of the two option menus, the “Use Filters” button and the “Today” button to line up nicely. This tool appears automagically in the object inspector when you select multiple objects. Yet another way Revolution anticipates what I probably want to do in a given situation and makes it easy for me to do so. (Have I told you yet how much I like working in Revolution?)

Page 291: Software at Speed of Sound -- Dan Shafer

266

Also, a word of explanation of the “Use Filters” button is in order. There are two states in which I can be viewing my list of tasks: filtered or unfiltered. I could use two radio buttons here, but it seemed to me that would disrupt the aesthetics; I’d either have to put them next to the checkboxes, stack them on top of one another at the right edge where I have the button now, or perhaps even create a third “line” of UI components under the field. None of those seemed very attractive.

But I know that in Transcript, I can dynamically change the label on the button. So my plan is to have the label on the button toggle as I change the view. So when I’ve selected to use the filtered view, I’ll change the button’s label to “All Tasks” and when I’ve selected “All Tasks,” the button will say, “Use Filters.” Lots of UI designers would cringe at that decision and you can certainly decide to change my design if you like, but I think it’s kind of cool and it serves the purpose.

By the way, there’s another approach I could have taken here. I could have replaced all those UI controls with a single button to pop up a palette (which is just another substack in my application that I treat as a palette). That seemed like overkill here to me, but if you want to try to design it that way yourself, you can because (here I go again) you own the software!

As I create each of these controls, I give them meaningful names. I also assign labels to the popup menu buttons from which I can make my choices for selecting and sorting criteria.

The “Sort by:” popup menu in the upper right is named SortOptionButton. Its label is set to “Select...” and its menu items are set to: Select..., Due Date, Priority, and Status.

The Task Type checkboxes are named, from left to right, filterCurrent, filterPast, and filterFuture.

The “Lowest Priority” popup menu is named PriorityFilter, with its label set to “D” and its menu items to, in this order, D, C, B, and A.

The “Status” popup menu is called StatusFilter. It has a label set to “All Tasks” with menu items labeled Pending, Completed, and On Hold.

Creating the To Do List Item Management Area The bottom portion of the screen is devoted to dealing with individual tasks. The idea is that I select a task from the list and its details appear in this area. This is also where I create new tasks, filling out all their details.

Given what I said in the program description I wanted to accomplish here, I think we clearly know that we need the following controls:

Page 292: Software at Speed of Sound -- Dan Shafer

267

• editing area for the task description (a non-scrolling field since I want to keep task descriptions relatively short)

• editing area for the note associated with the task (probably a scrolling field)

• control for setting the priority

• editing area to place the start date (though I’ll use the calendar to enter the date)

• editing area to place the due date (though, again, I’ll use the calendar for data entry)

• control for setting the status (which will default to “Pending,” for obvious reasons)

I’ll also need some way (buttons presumably) to:

• create a new task

• delete a task

• mark a task complete

• mark a task “on hold”

• update the task in the To Do list after editing (or add a newly created task to the list)

That’s a lot of UI componentry, but we have a large amount of space with which to work and we haven’t set any constraints on the height of the application window (at least not yet).

I’m going to begin by inserting a horizontal rule in the window to separate more clearly the item management area of the screen from the top area. I just like this aesthetically.

I grab the line tool (see Figure A-9), drag it to a point near the left edge of the window and, while holding down the Shift key to keep the line straight, drag nearly to the right edge of the window. Then I use the object inspector to set the size of the line to 2 pixels.

Page 293: Software at Speed of Sound -- Dan Shafer

268

Figure A-9. Tool Palette With Line Tool Selected

I start my layout by roughing in the frame. I place the one-line field for the task description in the upper left portion of the area, leave enough space for a reasonably wide scrolling notes field at the right, then drop in the field. I may well change the locations and sizes of these objects as I go along, but this gives me a nice framework within which to work. The design so far looks like Figure A-10.

Page 294: Software at Speed of Sound -- Dan Shafer

269

Figure A-10. Task Management Area Roughly Framed

Now I want to put the controls that display the characteristics of the selected task, namely the priority, start date, due date, and status. Here, I can use the Duplicate command (Command/Ctrl-D) to select and copy the controls I used in the list management area for priority and status and have a head start creating them. I’ll need to edit the contents of each one so that, for example, the “Select...” option doesn’t appear but rather the default values I want to assign to new tasks are shown. I use the area below the task description editing field to display these characteristics.

Then I lay out the four buttons to manage the selected task in a single-line array below the property displays.

In the process, I use a couple of interesting tricks. After creating four buttons, I label them. One of them, “Mark Complete,” has a label that is slightly wide for the default size of a button. So I select it, enlarge it, then shift-click on the other three, resulting in all four buttons being selected. Now in the object inspector, I go to the “Align Objects” pane and select the control (see Figure A-11) that tells Revolution to match up the widths of the buttons.

Page 295: Software at Speed of Sound -- Dan Shafer

270

Figure A-11. Equalizing Widths of Four Selected Buttons

Now I deselect all of these buttons and I click on the left button (“Update List”), then shift click on each of the others in sequence, ending with the “Delete Task” button. Back in the object inspector, I click on the left icon in the “Distribute:” section and select “First to last selected” from the popup menu (see Figure A-12). Now the buttons are neatly distributed in the space I’ve allocated to them and they’re all the same width. Very tasty.

Page 296: Software at Speed of Sound -- Dan Shafer

271

Figure A-12. Distributing Buttons Within Their Allocated Space

The finished result looks like Figure A-13. Notice that I put the “New Task” button in the lower right corner where users expect to see “OK” and “Cancel” buttons because adding a new task will be a fairly common undertaking and this seems like a good place for it, at least for now. I’ve also made this button the default button on the layout. I may well re-visit that later.

Page 297: Software at Speed of Sound -- Dan Shafer

272

Figure A-13. First Version of ToDoPlus User Interface Complete

This design might or might not win any UI prizes, but it’s functional, clean, understandable, and concise. It will certainly do well for my initial purposes, subject as always to my tweaking as I explore the application.

Now it’s time for the fun stuff: programming the functionality of ToDoPlus.

Programming ToDoPlus Functionality If I take a 10,000-foot view of ToDoPlus, I’d start my actual scripting work by making it possible to create new tasks. This would give me the ability to build a small test file to use on the rest of my development work. Then I’d work on the task-editing functionality, including task deletion, followed by the filtering and then the sorting (order doesn’t matter so much there), and finally the printing.

But I have three preliminary things I have to do to get myself to a place where it makes sense to create tasks at all.

First, I have to create the text file into which I’m going to place the newly created tasks. Now, if I were developing a commercial application here, I’d do a lot of fancier footwork to set up the application so that when it launches it checks for the existence of the file and, failing to find it, creates it automatically. But remember, I’m building this for me. I

Page 298: Software at Speed of Sound -- Dan Shafer

273

don’t need no stinkin’ file-checker code. (I’ll leave the job of creating such code for you as a dreaded “exercise for the reader.”)

Second, I have to get the calendar object working so that I can have it assign start and due dates to my newly created tasks. I could, of course, postpone that step and just manually enter the start and due dates as I create new tasks; my application doesn’t yet know anything about how I plan to do that. But that feels somehow untidy. Also, I’m a little tired of my calendar being blank anyway. So I’ll tackle that task up front.

Third, I have to set up the application environment to be ready to receive new tasks. This involves reading the contents of the file containing the tasks, initializing some variables I’ll need as I work with tasks, and cleaning up the user interface contents as appropriate.

Creating the To Do List File

Creating the file is trivial, of course. I can do it in any of several ways. I can just use my favorite text editor to create a new, empty file, give it the name “MyToDo.txt” and I’m done. I can also type the following command into the message box in Revolution:

open file “MyToDo.txt”

Since the file doesn’t exist, Revolution will create it. It will also create it automatically in the directory where Revolution expects to find your files. Later, if I want to build a distribution of the ToDoPlus stack for my wife or a fan (yeah, I have a few fans, wise guy!), I will have to remember to put the text file in the project.

When I begin creating the application startup routines I mentioned above, it would be useful to have at least one item in the file so that I can ensure that those routines actually do what I expect. I don’t yet have a programmatic way of adding information to the text file, but I can do it through the Message Window.

I just type the following line into the message box and hit Enter. The file will then contain one task I can work with as a starting point. Notice that I set this task’s “order” item (the first item in the line) to 1 since it’s the first and so far only task in the file.

put “1|Task A|A|Pending|8/31/03|8/31/03|Note for Ta sk A” into URL “file:MyToDo.txt”

Just for good measure, I’m going to close the file now, too. This is not strictly speaking required; Revolution closes any open files when you quit it or your application. But I like to err on the side of caution. No sense having any dangling file stuff hanging around.

close file “MyToDo.txt”

Page 299: Software at Speed of Sound -- Dan Shafer

274

Getting the Calendar to Display and Respond to Clic ks

OK, let’s start with the question of why the calendar comes up blank to begin with. For a clue, I’m going to open the objCalendar stack from which I copied and pasted the group. It behaves as I expect, so clearly there’s something I’m missing.

I see that there’s a button in this stack labeled “Display Calendar.” I open its script (don’t you love the way you can learn how to do things by examining the work of others?). The script looks like this:

on mouseUp send "calendar_display" to group "objCalendar" end mouseUp

Aha! So the trick is to tell the calendar you want it to display. Duh! This ought to be a piece of cake. When do I want the calendar to display? Why, as soon as I open the stack. So I’m going to put the send command above into an on openStack handler. I copy the line, open the stack script (from the Object menu), type “on openStack” and press Return and then place the line into the script. The result looks like Figure A-14.

Figure A-14. Stack Script openStack Handler

The easiest way to test this script to see if it has the desired effect is to type the following into the message box, so I do.

send “openStack” to stack “ToDoPlusMain”

Yep. Just works. One of these days, I’m sure I’ll get over the giddy feeling that comes when I do some programming and it just works as expected the first time. Java never

Page 300: Software at Speed of Sound -- Dan Shafer

275

did that for me. Smalltalk did some of the time. Python did most of the time. Transcript does almost all the time.

So now I have a calendar that displays the current month’s calendar, highlights the current week and clearly marks today’s date. That was too easy!

I’m pretty sure that if I click on a date, I’m not going to see anything useful happening, since I haven’t yet told the Calendar what to do when I click on it, so I’m going to open the object’s script and peruse it a bit to get a feel for what it can do and, ultimately, how to get it to do what I want it to do. As I have built the application, I should click on a date and expect the Calendar object to highlight that date and do nothing else until and unless I click on one of the two buttons that set start and due dates. Then I’ll want to be able to retrieve the selected date and put it into an appropriate field in the item-editing region of the layout.

I open the script of the Calendar object and, from the excellent comments at the beginning of the file (thanks, Shao Shen!), I determine that I want to look at the handler called calendar_dateClicked. Using the script editor’s Find capability, I locate the handler. It looks like this:

on calendar_dateClicked pDateItems # Description..: stores the dateItems of the date clicked set the opaque of button ("date" & item 7 of currentMonthArray["da teItems"] + item 3 of clickedDateItems -1) to FALSE put pDateItems into clickedDateItems -- store the dateItems of the date clicked set the opaque of button ("date" & item 7 of currentMonthArray["da teItems"] + item 3 of clickedDateItems -1) to TRUE pass calendar_dateClicked -- pass for developer trapping end calendar_dateClicked

Line 1 is a comment. Lines 2 and 4 concern themselves with the way the clicked date looks on the screen. The real work of the handler that I care about at the moment is in Line 3. It stores the contents of the variable pDateItems in a variable called clickedDateItems. The handler then uses a pass command to send the calendar_dateClicked message up the message-passing hierarchy so I can trap it and do whatever I like with it.

In the comments for the script file, I read that when the calendar_dateClicked message is passed, the pDateItems parameter is passed as well. (This would be the case in Transcript with no specific effort on the developer’s part.) So what I need to do is simply define an on calendar_dateClicked handler at the card level or higher in the Revolution message-passing hierarchy and store the currently selected date in a way that I can retrieve it on demand. I never need to track more than one currently selected date, so a simple global variable will do nicely here. I’ll call the variable currentSelectedDate and create a card script handler that looks like this:

Page 301: Software at Speed of Sound -- Dan Shafer

276

on calendar_dateClicked pDateItems global currentSelectedDate put pDateItems into currentSelectedDate put currentSelectedDate -- for testing only; dele te in final version DGS end calendar_dateClicked

I click the Apply button and then click on a date in the calendar. Sure enough, the Message Box displays the seven-item, comma-delimited text that reflects the selected date’s values (see Figure A-15).

Figure A-15. Message Box Displaying currentSelectedDate Value

(In the Message Box shown in Figure A-15, I’ve selected August 31, 2003. The year is the first item, month is the second, day is the third; the rest of the items are no of interest to me in this application. See Chapter 10 if you want to know more about dateItems and what’s going on with them.)

One thing needs to be addressed. I don’t want to put “2003,8,31” into the date fields in the window when I select dates from the calendar object. How do I get that information into a more user-friendly format?

Well, I know I’m dealing here with information in a dateItems object, so I’m going to pop open the Revolution documentation, check the Transcript Dictionary for dateItems and see what shows up. Aha! in the “See Also” drop-down menu, there’s a “convert command” entry. Sounds promising. So I click on it and, sure enough, it’s exactly what I need.

From the documentation, I conclude that I want to store dates in my application in the format Revolution refers to as the short date. So I edit my card script so the on calendar_dateClicked handler looks like this:

on calendar_dateClicked pDateItems global currentSelectedDate convert pDateItems to short date put pDateItems into currentSelectedDate end calendar_dateClicked

OK, so now I have things set up so that a global variable -- which will be accessible to me from my button scripts -- has the currently selected date stored in a useful form.

Page 302: Software at Speed of Sound -- Dan Shafer

277

Creating a New Task When I want to add a new task to the ToDoPlus file, I will expect to do the following things:

1. Click on “New Task”

2. Type a description of the new task

3. Set its priority

4. Change its status if necessary (but the default of “Pending” should work almost all the time)

5. Select a date from the calendar and click on “Set Start Date” (I won’t have to click on a date if today’s date is selected, as it will be when the application starts.)

6. Select a date from the calendar and click on “Set Due Date”

7. Add a task note if I have one

8. Click on “Update List”

Aside from the first and last steps, the other steps can be done in any order I like.

When I click on “New Task,” I want to be sure all the fields are in their pristine default conditions so that if I’ve been working on a task at the time I clicked the button, I’m not faced with having to delete and reset things manually.

When I click on “Update List,” I will store the new task information internally (I’ll have more to say about that shortly) and update the To Do scrolling list. I’m also going to write the revised list to the disk file for reasons I’ll explain shortly.

Setting Up for a New Task to Be Entered I’ll want to clear four fields and set the values of the priority and status menus. To do that, I’ll have to know the names of these objects. Figure A-16 provides these names so you can follow along with the scripting discussion that follows.

Figure A-16. Names of Objects Used in “New Task” Button Script

Page 303: Software at Speed of Sound -- Dan Shafer

278

As I think about the script I want on the “New Task” button, it occurs to me there might be other times I’ll want to reset all of this task-related stuff to a base point as I do here. So rather than stuff the script into this button where it would be harder for other objects to access it, I’m going to put the script at the stack level and just call the handler from the New Task button’s on mouseUp handler. I’ll call it initTaskRegion.

The handler for the New Task button, then, is simplicity itself:

on mouseUp initTaskRegion end mouseUp

In the stack script, I place the initTaskRegion handler, which looks like this:

on initTaskRegion put empty into field "taskDescriptionField" put empty into field "taskNoteField" put empty into field "startDateField" put empty into field "dueDateField" set the label of button "priorityMenu" to "A" set the label of button "statusMenu" to "Pending" end initTaskRegion

Nothing tricky going on here.

Back in the on mouseUp handler for the New Task button, I want to do one more thing. I hate it when I have to click into a field to begin entering data. I have been known to scream at the computer, “You’re a computer! You’re supposed to be smart! Figure out where I need the stupid cursor and just put it there, would you?” I try to avoid such outbursts and I’m getting better as I get older. But in Transcript, it’s so easy to fix this problem. I just added the following line as the second line of my handler:

select before field “taskDescriptionField”

OK, now I’m all set up to create a new task in ToDoPlus, except for selecting and inserting start and due dates. Let’s take care of that right now.

Connecting the Date-Setting Buttons

I’ll open the script of the “Set Start Date” button and put the following script into it. It’s pretty straight-forward, so I don’t think I need to spend any time explaining it except to remind you that global variables hold their values throughout the application as long as you refer to them in each handler that needs them.

on mouseUp global currentSelectedDate

Page 304: Software at Speed of Sound -- Dan Shafer

279

put currentSelectedDate into field startDateField end mouseUp

It doesn’t take a lot of imagination to figure out that the mouseUp handler for the “Set Due Date” button will be almost identical:

on mouseUp global currentSelectedDate put currentSelectedDate into field dueDateField end mouseUp

I test these and they behave as expected.

Setting Up the Application at Launch The final step I need to take to get the ToDoPlus application ready to receive new entries is to handle the application start-up activities.

When the application launches, I want it to:

• load the contents of the MyToDo.txt file into memory so I can work efficiently with the data

• display the task descriptions of all the items in that file, in the order in which they appear in the file

• return the settings of all the task management area controls to their default conditions

• initialize any variables I’m going to need in other handlers

Loading MyToDo.txt Into Memory As is my usual style, I’ll create a handler to call from the openStack handler that takes care of all this work. This keeps my design and code modular and makes maintenance easier than if I slam all of the code into one long (and potentially ugly!) handler.

I’ll call this handler initializeMainWindow. Here is the initial code I’ll place in it:

on initializeMainWindow global theList, order put URL "file:MyToDo.txt" into theList put the number of lines of theList into order end initializeMainWindow

The first line defines the two variables I already know about that I’m going to want to use throughout the application and which I need to use in this handler, to be global variables.

Page 305: Software at Speed of Sound -- Dan Shafer

280

The second line of the handler opens the file and reads its contents into the variable theList. This is the variable in which I’ll store all the tasks in memory as I work with them in the application. The third line counts the number of lines in theList and places that number into the variable order, which I’ve already explained. This allows me to add 1 to this number when I create a new task.

There are two more initialization tasks to be taken care of before I worry about the task management region clean-up.

First, I have to make sure that all of the lines in theList are properly numbered in their first item position. Recall that the order can change as I work with editing and deleting tasks, which could result in the file not containing a good, known starting display order for the items when they are loaded.

I’ll define a new handler called initializeListOrder, which I’ll call from initializeMainWindow. The code will look like this:

on initializeListOrder global theList, order set the itemDelimiter to “|” repeat with k = 1 to the number of lines of theList put k into item 1 of line k of theList end repeat end initializeListOrder

This is pretty simple stuff. I just loop over all of the lines stored in theList and put “1” into item 1 of the first line, “2” into item 2 of the second line, and so forth until I’m done.

Second, I need to display the second item (description) of each of the lines in theList in the To Do List field. I’ll call this handler updateListField. It looks like this:

on updateListField global theList set the itemDelimiter to "|" put empty into field ToDoListField repeat with lineNum = 1 to the number of lines in theList put item 2 of line lineNum of theList & return after field ToDoListField end repeat end updateListField

Notice that I choose to display only the description of the task in the To Do List scrolling field. I could have made that field a table and displayed more bits of data, but I’m happy with the ability to examine each of the tasks individually in the task management region of the screen. I like the uncluttered feeling of displaying only the description, which is, after all, probably how I best know the task anyway.

Page 306: Software at Speed of Sound -- Dan Shafer

281

The final application setup task is to make sure that the task management area at the bottom of the screen starts out in pristine condition. It is worth noting that if I were building a standalone program for other people to use, I wouldn’t have to worry about this because any contents of these controls that were present when the user last quit the application would not be stored anyway. But since I’m using this application in the Revolution environment (or at least I want to be free to do so), I need to clean up any cruft that might be left over from the last run.

I’ll write a handler called initTaskRegion that handles this assignment. Here’s its self-explanatory code:

on initTaskRegion put empty into field "taskDescriptionField" put empty into field "taskNoteField" put empty into field "startDateField" put empty into field "dueDateField" set the label of button "priorityMenu" to "A" set the label of button "statusMenu" to "Pending" end initTaskRegion

If this code all works as expected, I should be able to launch the ToDoPlus application and have it come up with the task list displayed and the bottom area of the window cleaned up.

I type the following line into the Message Box and, as you can see from Figure A-17, it works exactly as expected. send openStack

Page 307: Software at Speed of Sound -- Dan Shafer

282

Figure A-17. Opening Window of ToDoPlus After Initialization Routines Work

Adding the New Task Now that I’m all set to enter the data for a new task, I need a way to put the new task into the To Do List scrolling field in the upper right corner of the window. I know that before I’m done I’m going to want to keep track of the items and their various characteristics in my application’s memory. That will make accessing, sorting and filtering on it much faster than if I rely on constant disk file reading and writing. On the other hand, I’ve set the application up so I don’t have to explicitly save the list into the file, so I need to handle that task. So any time I have a change to the list, I’m going to want to trigger an operation to update the file and keep it in synch with what’s stored in memory about the To Do list.

This means that the “Update List” button needs to update the memory contents stored in theList, the displayed list (which is only showing one portion of the data and perhaps not even all of the tasks) and the file.

Since the “Mark Complete,” “Hold Task,” and “Delete Task” buttons will also update the in-memory version of the list and the file, I want to write handlers that will deal with those tasks and that can be called from any object that needs to use them.

Page 308: Software at Speed of Sound -- Dan Shafer

283

But I have an intriguing design decision to make between two annoying outcomes. As long as I’m only adding a new task to the To Do list when I’m viewing the list in its entirety, I can just stuff it at the end of the list. But what if I’ve got some filtering criteria in place and I’m only viewing part of the list at the moment? And what if, just to make things really interesting, I add a new task which, if the current filters are applied, won’t even display? That seems like a bad design idea. So I have a choice between designing the user experience for adding a new task so that it resets all my filtering criteria and displays the whole list in its original unsorted order or I add the new list and, if the filtering citeria make it invisible, just don’t show it.

Neither outcome is very desirable, but I decide to opt for the first approach. This also means I need to create a dialog box to remind myself that if I add a new task, I may change my view of the To Do list so that if I want to back out before I mess up the display, I can.

(It seemed so simple a few minutes ago to add a new task, didn’t it? That’s the way softwsare goes sometimes.)

When I get ready to write a new task to the file, I have to do the following things:

1. confirm that it’s OK to reset the To Do list view

2. assign the new task an order that is one higher than the number of items currrently in theList

3. gather each element of the item together, placing a “|” character between each element

4. put this new line after the current contents of theList

5. sort theList by the “order” item to ensure that the new item displays where it should based on current list settings

6. reset all the filter controls to their default conditions

7. put the second item of each line of the revised copy of theList into the scrolling list field

8. write the entire new version of theList to the MyToDo.txt file

Now, before you jump up and down and start yelling about how inefficient this is going to be, hold on for a sec.

First, remember: I’m the only one using this. I don’t expect my list ever to have even as many as 100 elements in it. So efficiency’s not high on my list of feature requirements.

Second, by automagically writing the updated file each time, I ensure that if something happens and my (totally bug-free, of course) application crashes or my battery dies or any of 100 other things goes wrong (picture small dog named Einstein leaping onto keyboard), my data will always be current.

Page 309: Software at Speed of Sound -- Dan Shafer

284

Here’s the script for the “New Task” button as it now stands:

on mouseUp global theList, order initTaskRegion put the number of lines in theList + 1 into order select before field taskDescriptionField end mouseUp

Notice that I call initTaskRegion, a handler I defined in the stack script earlier. The last line of the script places the cursor into the field I’m most likely to begin typing in as soon as the application is ready to accept a new task.

Updating theList will be handled by a stack script handler called update_theList. It looks like this:

on update_theList global theList, order put order & "|" & \ field "taskDescriptionField" & "|" & \ the label of button "priorityMenu" & "|" & \ the label of button "statusMenu" & "|" & \ field "startDateField" & "|" & \ field "dueDateField" & "|" & \ field "taskNoteField" into line order of theL ist end update_theList

Remember that the backslash (“\”) is used to continue a long line in the script editor.

The first line declares the global variables theList, as I described it above, and order. The second line assembles all the elements of the task currently displayed in the task management region of the window, separating each with a vertical pipe, puts a carriage return at the end so it’s all on one line and by itself, and then puts it at the end of theList. The third line is just there for testing purposes.

I throw some meaningless (and totally uninspired) sample data into the bottom part of the screen, click on the “Update List” button and nothing happens. Ah, right. Must be getting late. Open the script editor on the “Update List” button and put this script in place, save it, and then try it, I tell myself:

on mouseUp update_theList end mouseUp

I’m going to create another stack handler for a message called updateListField. Even though it’s true that for now I only need very little code to make this script do what I need to have done, I know that once I get to using filtering and sorting, this is going to

Page 310: Software at Speed of Sound -- Dan Shafer

285

get a whole lot more complicated. So I break it into a separate handler now to make it easier to extend later.

Whenever I need to update the To Do list in the scrolling list field, I’ll simply replace the present contents with the second item of each line in the variable theList. So here’s how I’ll start the updateListField handler:

on updateListField global theList set the itemDelimiter to "|" put empty into field ToDoListField repeat with lineNum = 1 to the number of lines in theList put item 2 of line lineNum of theList & return after field ToDoListField end repeat end updateListField

I first change the value of the global Revolution setting called itemDelimiter to be the vertical pipe. Normally, as you know, this delimiter is a comma. By resetting it to the pipe, I can use the built-in Transcript mechanisms of dealing with items in a single variable. Otherwise, I’d have to write my own parsing routine to look for the vertical pipes and extract data accordingly. Another great feature of Transcript!

Updating the File Whenever theList gets changed, I want to write it to the file MyToDo.txt. It turns out that Revolution makes this task ridiculously easy as well. No need to open the file, explicitly record information, keep track of stuff, and close the file when I’m done. I can just use the put command with the URL option like this:

put theList into URL “file:MyToDo.txt”

This line automatically replaces the contents of the file “MyToDo.txt” with what is presently stored in the global variable theList. If the file didn’t exist, this command would also create it. (Yeah, this means that I could have avoided creating the file earlier and if I were building an application for multiple people to use, I would have done so.)

Again, I’ll create a stack handler called updateFile that looks like this:

on updateFile global theList put theList into URL "file:MyToDo.txt" end updateFile

Now we need only add calls to these two new handlers to the mouseUp handler of the “Update List” button so that its final script now looks like this:

on mouseUp

Page 311: Software at Speed of Sound -- Dan Shafer

286

update_theList updateListField updateFile end mouseUp

That’s all there is to this process. Complicated, eh?

Editing, Updating and Deleting Tasks Once I have my To Do list stored in the MyToDo.txt file, I want to be able to do things with items stored there. My design specifically calls for the ability to:

• change the status of a selected item to “Complete”

• put a task on hold

• delete a task

You might wonder why I included buttons for the first two tasks. After all, I can just select a new status for the selected task from the Status menu, click on “Update Task” and accomplish the same thing, right? Yes, you’re right. But I’m a one-button kind of guy when it comes to frequent tasks. I just find the ability to click on a button and have a multi-step -- even just a two-step -- process complete to be useful.

Displaying the Task to Be Edited I intend the UI to behave intuitively. If I click on a task in the task list, that task appears in the task management region of the window. This should be pretty easy because it’s essentially a reversal of the process of assembling a new task from data entered when creating a new task. This time, instead of gathering the pieces together and separating them with pipes, I’ll want to look at the appropriate line in theList, break it into items separated by pipes and put the data into the proper controls in the task editing region.

Now I should be able to write a mouseUp handler for the task list field that selects the right line from theList to break apart and display in the task management region of the window. Because ToDoListField is a scrolling field, it is automatically locked so that it generates the mouseUp message when I click in the field.

Here’s the code for the field’s mouseUp handler.

on mouseUp global theList, order put word 2 of the clickLine into currentTask set the itemDelimiter to "|" put item 1 of line currentTask of theList into or der put item 2 of line currentTask of theList into field " taskDescriptionField" set the label of button "priorityMenu" to item 3 of line current Task of theList set the label of button "statusMenu" to item 4 of line currentTa sk of theList put item 5 of line currentTask of theList into field " startDateField"

Page 312: Software at Speed of Sound -- Dan Shafer

287

put item 6 of line currentTask of theList into field " dueDateField" put item 7 of line currentTask of theList into field " taskNoteField" end mouseUp

When I click on a line in the field and then call the clickLine function, Revolution generates a chunking expression that looks like this:

line 3 of field “ToDoListField”

I’m only interested in the line number, so I pull out the second word of the expression returned by the function and stuff it into a variable called currentTask. I use this value in the remaining lines to extract each item of theList in which I’m interested and place it into the appropriate control in the task editing region.

Now, when I select an item from the To Do list, it populates the task management region of the screen.

I restart the application. I notice that the To Do list has my sample data in it.

Next, I click on one of the tasks in the list. The fields in the task manager region of the window populate, labels change as appropriate. I make a change in the description of my test task (because that’s the only thing being displayed in the To Do List above), click on the “Update Task” button and, lo and behold, the task description updates in place as expected.

Figure A-18 shows how the window looks now with data in it and a task selected for editing in the task management region. It has now transcended the imaginary border and become a fairly usable application. It still doesn’t let me delete a task and none of the sorting and selection stuff works yet, but I could use the application as it now stands and have a better To Do List manager that I own than I had when I started less than an hour ago.

Page 313: Software at Speed of Sound -- Dan Shafer

288

Figure A-18. ToDoPlus Application With Task Ready to Edit

Updating Task Status Obviously, I can change the status in the status menu button, click “Update List” and be home free as far as marking tasks complete or putting them on hold. But, as I said earlier, I’m kind of a stickler for ease of use, so I’m going to add scripts to those buttons so they take care of this two-step process in one click.

First, I’ll code the “Mark Complete” button. Here’s the script I bet will work the first time I try it.

on mouseUp set the label of button "statusMenu" to "Complete d" update_theList end mouseUp

No messing around with currentTask because the value of that variable doesn’t change as a result of what I’m doing here. No need to update the field, either, because the value I changed isn’t being displayed.

I copy and paste this handler into the script of the “Hold Task” button, change the value I’m using for the label on the button, apply it and test it. It works as expected. Here, for the sake of completeness, is that handler.

Page 314: Software at Speed of Sound -- Dan Shafer

289

on mouseUp set the label of button "statusMenu" to "On Hold" update_theList end mouseUp

Deleting a Task Deleting a task is a little tricky because I have to make sure the order of the display is updated correctly and that the order associated with all of the tasks in theList below the one I’m deleting are updated correctly.

But before I just skip right past this, let me think for a moment. What if I inadvertently hit the “Delete Task” button? How would I recover from that? I’d have to remember the task I deleted and re-enter it as a new task. Ugly.

Let’s put a confirmation dialog into the process. I can handle this one of two ways. I can create a one-card stack that can then be displayed using the modal command and act as a modal dialog or I can just use a standard Revolution answer dialog. If I use the former method, I can optionally invoke the new stack’s appearance with the sheet command, which causes it to appear as a window sheet in Macintosh OS X but as a modal dialog on all other platforms.

In a commercial application, I’d almost certainly create a custom message, use a stack, and invoke it using sheet. But I’m the only user here, so I’m going to take the easy way out and use a standard system dialog box. I will display the dialog box asking myself if I really want to delete this task. (I talk to myself fairly often, so this is not an unusual construct for me.) If I click “OK”, I will delete the item and send the mouseUp to the “Update Task” button. If not, I’ll just stop processing the command.

Here’s how I code this functionality.

on mouseUp global theList ask "Are you sure you want to delete this task? C annot be undone!" as sheet if it is not empty then set the itemDelimiter to "|" put word 2 of the selectedLine of field "ToDoLi stField" into lineToDelete delete line lineToDelete of theList repeat with k = lineToDelete to the number of l ines of theList if item 1 of line k of theList is not 0 then subtract 1 from item 1 of line k of theList end if end repeat updateListField end if end mouseUp

Page 315: Software at Speed of Sound -- Dan Shafer

290

I now have a fairly working application. It lets me add new tasks, edit existing tasks, change the status of tasks with one button, and delete tasks.

Next, I’ll turn my attention to the filtering and sorting of tasks. I’m nearing the home stretch; once this set of features is working, all I have to implement is printing and I will have an application that meets my original design specifications.

And so far I have only about an hour and 15 minutes of work involved.

Filtering and Sorting the To Do List Right off the bat, I recognize I have a potential problem I’d better deal with. If I select some filters and then quit the application, the next time I open the application, all tasks will be displayed because that’s the defauklt behavior but the checkboxes and menus on the screen may indicate that some filtering is in effect. On a long day, this could confuse me beyond all possibility of recovering my sanity.

This is of course an easy problem to fix. I just add some code to the initalizeMainWindow handler in the stack script to address the tidying up that needs to be done. As usual, the newly inserted code is in bold.

on initializeMainWindow global theList, order put URL "file:MyToDo.txt" into theList put the number of lines of theList into order set the highlight of button "filterCurrent" to tr ue set the highlight of button "filterPast" to true set the highlight of button "filterFuture" to tru e set the label of button "PriorityFilter" to "D" set the label of button "StatusFilter" to "All Ta sks" set the label of button "ListButton" to "Use Filt ers" set the label of button "SortOptionButton" to "Se lect..." initializeListOrder updateListField end initializeMainWindow

How Filtering Works Filtering of task items on my To Do list to allow me to focus the display on those tasks in which I’m interested at the moment is a cumulative process. Put another way, I’m using AND logic: every one of the criteria I set in the filtering controls must be met for a task to be displayed.

The order with which I deal with the filtering criteria, then, doesn’t much matter.

Page 316: Software at Speed of Sound -- Dan Shafer

291

Now, exactly how am I going to handle the process of tracking which tasks should be examined at each succeeding stage of the process? After a bit of thought, I realize I can use the first item in the task’s entry in theList -- what I’ve been calling the “order” -- to keep track. I’ll start out by giving all of the items in theList a value of zero in that item.

Then I’ll look at the first filter criterion and loop through all the items in theList to see if each meets that criterion. If so, I’ll switch its order value to 1. Then I’ll sort theList so all the items that have passed the filters so far are at the top.

I’ll check to make sure there is at least one remaining qualified entry in theList (if not, no amount of subsequent filtering is going to create one and I’ll simply notify myself that no tasks meet my criteria). If so, I’ll check the next criterion. And so forth until I either find myself with no matching tasks or I’ve checked all the criteria.

Finally, I’ll call the as-yet-nonexistent sort_theList handler to sort those items that are to be displayed.

Programming the Filtering Process The process of filtering the displayed To Do list in ToDoListField starts when I click on the button labeled “Use Filters.” This button changes its label from “Use Filters” to “Show All” depending on the state in which things are. If I’m looking at a filtered list, the button says “Show All” and vice versa.

In a full-blown, bullet-proof commercial application, I’d probably set up a routine to check to be sure the user had selected at least one criterion for filtering before I do any processing. In this case, I’m going to trust that the user (that’d be yours truly) is sufficiently alert not to ask the application to filter based on no criteria being chosen.

So I’m going to approach the filtering as follows:

1. Check to see that at least one of the Task Type checkboxes is checked. If not, I’ll pop up a dialog box reminding myself that I have to have at least one of those checked or nothing is going to be selected. When I finish slamming my forehead into the desktop (the wooden one, not the computer one), I’ll set the button back to “Use Filters” and pretend like nothing happened.

2. Assign all tasks in taskList an order value of zero.

3. Check each task type checkbox in turn to see if it’s on. If so, search theList and find any tasks whose due date falls into an appropriate range to meet that criterion, changing their order value to 1 as I find them.

4. Sort theList.

5. Look at the Lowest Priority menu setting. If it’s “D”, skip this step because all tasks have a priority of D or higher by definition. If it’s any other value, loop through those elements of theList that are still candidates for display (i.e.,

Page 317: Software at Speed of Sound -- Dan Shafer

292

they have a “1” in their order item) and turn off any that have a priority lower than the boundary setting.

6. Sort theList.

7. Look at the Status menu setting. If it’s “All Tasks,” skip it (which means I’m done filtering and can now just sort the list and display it). Otherwise, loop over the lines of theList with an order value of 1 and ensure that each has the appropriate status as displayed in the Status menu. Change those that don’t match to a zero value.

8. Loop through those elements of theList that have a 1 in their order slot and change the numbers to be sequential from 1 to the number of items set to display.

9. Update the list view.

Later, I’ll change the code at Step 8 to sort the list according to the sort criteria, if any, I’ve selected. But for now, this default sort will do fine as I test my scripts.

My instinct as I write this code is to break it into a number of small handlers and then call those handlers from the mouseUp handler of the button that determines whether I’m using filters or not. There isn’t a really good technical reason for doing this; in fact, using modules will probably slow things down just a tad. But I’m betting the performance hit will be minimal and that using separate handlers will make it easier to test and debug, so I’m going to follow my instinct. If you decide to write one humongous handler for this set of filter criteria, I promise not to hold that against you.

I’ll put all these handlers in the button script. That will enable me to copy and paste this button into other applications and bring its functionality along with it, if I want to do so.

Here’s the basic mouseUp handler for the button. I’ll write each of the listed handlers in the order in which they appear here.

on mouseUp global theList if the label of me is "Use Filters" then if typeIsChecked() then -- at least one task ty pe is checked unsort_theList filterByType filterByPriority filterbyStatus if not checkIfDone() then sort_theList updateListField set the label of me to "Show All" exit mouseUp else answer "No more items after filtering" as s heet exit mouseUp end if

Page 318: Software at Speed of Sound -- Dan Shafer

293

set the label of me to "Show All" -- we are d isplaying with filters now else answer "You must select at least one Task Typ e, Shafer!" with "Doh!" \ as sheet set the label of me to "Use Filters" end if else -- I was set to "Show All" answer "This will remove all current filters an d sorts" with "Cancel" or "OK" \ as sheet if it is "Cancel" then exit mouseUp initializeListOrder -- re-numbers list items in current order including 0's updateListField set the label of me to "Use Filters" end if end mouseUp

At the outer edge of the handler is an if-else clause that determines the current state of the button and invokes the proper functionality. If the label is “Use Filters” that means I’m asking the program to do its filtering and then change the label to “Show All.” If the label is already “Show All,” then I need to undo the filtering. In either case, when I’m done I need to call updateListField to ensure that the list is displayed correctly.

In the first portion of the handler, I call the function typeIsChecked to ensure that at least one type of task is selected. Here’s that handler:

function typeIsChecked if ((the highlight of button "filterCurrent" is t rue) \ OR (the highlight of button "filterPast" is t rue) \ OR (the highlight of button "filterFuture" is true)) then return true else return false end if end typeIsChecked

Remember that a function differs from a handler in two ways. First, it begins with the keyword function rather than on. Second, it must return a value. This one returns logical values true or false, which are Tranascript constants. That enables me to avoid having to check the value of the returned response in my mouseUp handler where I just write:

if typeIsChecked() then

If typeIsChecked returns true, this if condition is met and processing continues. If it returns false, the if condition is not satisfied and I point out to myself (rather rudely as you can see, but since I’m the only user I can afford that bit of pique) that I need to check at least one task type.

Page 319: Software at Speed of Sound -- Dan Shafer

294

(By the way, this is a good time to point out that I’ve switched modes in my script editing environment so I can find a specific handler in the increasingly large number of handlers that make up the button’s script. As you can see in Figure A-19, the handlers are listed alphabetically in the left pane of the two-pane window, with the scripts located in the right pane. You’ll find this is a very convenient way to work once any given object’s script contains multiple handlers.)

Figure A-19. Two-Pane Script Editing Window

You get to this layout by using the View menu with a script editor window active, as shown in Figure A-20.

Page 320: Software at Speed of Sound -- Dan Shafer

295

Figure A-20. Script Editor View Menu Showing Options for Two-Pane Editor

Having determined that at least one task type is checked, the first thing I need to do is to get theList’s order items into a known state from which I can begin modifying them. So I write a handler called unsort_theList. It looks like this:

on unsort_theList global theList set the itemDelimiter to "|" -- Sets order to 1 for all items preparatory to f iltering repeat with k = 1 to the number of lines of theLi st put 1 into item 1 of line k of theList end repeat end unsort_theList

This routine sets the value of the first item (order) of all items in the list to a value of 1, so they all start out with the assumption that they will be displayed. (As you’ll see soon, I use this item to decide whether to display an item or not.)

Setup is now complete, so I can begin to process the filters. The first one I’ve chosen to tackle is the Task Type filter. This one defines whether I want to look at current tasks (i.e., tasks that are due today), past-due tasks (those with due dates in the past), and/or future tasks (those with due dates in the future). But I want to save a bit of time at the outset. If I’ve left all three checkboxes checked, then, of course, this filter handler won’t filter out anything, so there’s no point in having it run. Here’s the code for the filterByType handler as I created it:

Page 321: Software at Speed of Sound -- Dan Shafer

296

on filterByType global theList set the itemDelimiter to "|" if ((the highlight of button "filterCurrent" is t rue) \ AND (the highlight of button "filterPast" is true) \ AND (the highlight of button "filterFuture" i s true)) then exit filterByType -- all types are checked, no need to filter end if if the hilight of button "filterCurrent" is true then repeat with k = 1 to the number of lines of the List if item 6 of line k of theList <> the date th en put "0" into item 1 of line k of theList end if end repeat end if if the hilight of button "filterPast" is true the n repeat with k = 1 to the number of lines of the List if item 6 of line k of theList >= the date th en put "0" into item 1 of line k of theList end if end repeat end if if the hilight of button "filterFuture" is true t hen repeat with k = 1 to the number of lines of the List if item 6 of line k of theList <= the date th en put "0" into item 1 of line k of theList end if end repeat end if end filterByType

The code is pretty straight-forward and pretty much the same for all three checkboxes. I check to see if the box is highlighted. If so, I stuff the filter value into a variable, then loop over theList to see if each task’s appropriate item matches the value of the variable. If it does not, then I set its value to 0. (I used “1” as the default value, as you’ll recall, so now I have to use inverse logic to weed out those to be filtered.)

Having filtered the list by task type, I am ready to process the next possible filter, which is the priority filter. This popup menu is designed so that I define the lowest priority task I want to see displayed. So here I’m going to test the value in the variable against the actual value in theList using the greater-than symbol (>). Also, I don’t want to include any tasks in theList that have already been filtered out by the filterByType handler, so I have to check the first item to be sure it’s still got a 1 in it.

Here’s the code for the filterByPriority handler:

on filterByPriority global theList set the itemDelimiter to "|"

Page 322: Software at Speed of Sound -- Dan Shafer

297

if the label of button "priorityFilter" = "D" the n exit filterByPriority -- "D" includes all tasks end if put the label of button "priorityFilter" into min Priority repeat with k = 1 to the number of lines of theLi st if item 1 of line k of theList = 1 then if item 3 of line k of theList > minPriority then put "0" into item 1 of line k of theList end if end if end repeat answer "Priority filtered: " & theList -- debug D G$ end filterByPriority

The logic is identical to that used in the filterByType handler. Notice the second-to-last line of the handler, which uses the answer command to display the results of the list as it has been filtered now by type and priority. I left that here so I could tell you that this is a way I often work with handlers during development. I use an answer dialog to display the results at some intermediate processing point. This allows me to determine that things are going along swimmingly (or drowningly, as is all too often the case). I will, of course, remove this line when I finalize the application.

Finally, I filter the results according to the setting of the status menu. Here, an exact match against the values in theList will be required.

on filterByStatus global theList set the itemDelimiter to "|" if the label of button "StatusFilter" is "All Tas ks" then exit filterByStatus end if put the label of button "StatusFilter" into theSt atus repeat with k = 1 to the number of lines of theLi st if item 1 of line k of theList = 1 then if item 4 of line k of theList <> theStatus t hen put "0" into item 1 of line k of theList end if end if end repeat answer "Status filtered: " & theList -- debug DG$ end filterByStatus

I’m not quite done yet. Now I need to check to see if, after all this fancy filtering stuff, I have anything left to display. I wrote a function called checkIfDone, which I now call. If it returns true, then I know we have at least one task to display, so I call three other handlers (which I’ll get to in a moment) and change the label on the button, then exit the mouseUp handler. If it returns false, then I put up a dialog box that just tells me there’s nothing to filter and I leave everything as it was. This enables me to change a setting and try again without a lot of hassle.

Page 323: Software at Speed of Sound -- Dan Shafer

298

Here’s the code for the function checkIfDone.

function checkIfDone global theList set the itemDelimiter to "|" put true into finished repeat with k = 1 to the number of lines of theLi st if item 1 of line k of theList = 1 then put false into finished exit repeat end if end repeat return finished end checkIfDone

The code is pretty simple. But in the course of writing this handler, I had a valuable experience. I started out calling the variable that I’m returning to the calling handler done instead of finished. That’s why I called the handler what I did. But I got an error from Revolution telling me I couldn’t use done because it’s a reserved word in the Transcript language.

Bummer. But easy to fix.

Notice, too, that I use exit repeat to escape from the repeat loop as soon as I have the first task with a “1” in its first item because I don’t care how many such items there are as long as there’s at least one.

If the checkIfDone function returns true, I call a new handler called sort_theList, then our old friend updateListField (which turns out to need a bit of adjustment based on how we’re deciding what to display at this point).

Right now, theList has a bunch of lines representing tasks, each of which starts with either a 0 (meaning it’s not to be displayed) or a 1 (meaning it is to be displayed). But remember that my mouseUp handler for the field ToDoListField depends on the number in the first item of a task to match it up with the right task in theList for editing purposes. So I have to manipulate theList to preserve this correspondence.

Here’s the sort_theList handler.

on sort_theList global theList set the itemDelimiter to "|" sort lines of theList descending by item 1 of eac h repeat with k = 1 to the number of lines of theLi st if item 1 of line k of theList = 1 then put k i nto item 1 of line k of theList

Page 324: Software at Speed of Sound -- Dan Shafer

299

-- This should leave theList with items numbere d consecutively at -- the top until the first item with an order o f 0 end repeat end sort_theList

The third line of the handler sorts theList so that all the tasks with a first item with a 1 sort to the top.

Then the repeat loop simply numbers those tasks with a 1 in the first item from 1 to the number of displayed items that are left in theList.

Finally, I call updateListField, which we’ve seen before. But I have to make a slight change to it because as it now stands, it is ignorant of my brilliant scheme (well, OK, it’s not that brilliant and it’s not even really mine, but, what the heck) to use a 0 in the first item to supprress printing a filtered-out task. Here’s the revised handler, with the added code in bold as usual.

on updateListField global theList set the itemDelimiter to "|" put empty into field ToDoListField repeat with lineNum = 1 to the number of lines in theList if item 1 of line lineNum of theList <> 0 then

put item 2 of line lineNum of theList & return after field ToDoListField end if

end repeat end updateListField

The last thing I do is make sure the label on the button is set correctly.

In the last part of the handler, which executes if the button’s label was “Show All,” I just display a dialog box warning myself that I’m about to undo all the selections I have in place as well as the sorts I’m about to tackle. Unless I cancel the operation, I call the handler initializeListOrder in the stack script, which we’ve seen before. It just restores all of the items in theList to being numbered sequentially from top to bottom and then updates the field view of the list.

This does have the slightly unpleasant side effect of leaving the items in different order than they probably were when I opened the application and read the data from the file, but that’s pretty meaningless by now anyway and I can always sort the items the way I want to see them.

Which brings us to the next task in our little project: creating the sort script.

Page 325: Software at Speed of Sound -- Dan Shafer

300

Sorting the To Do List in the View I’ve indicated in my design that I want to be able to sort my To Do list in the field by due date, priority, and status.

The sort process itself is pretty simple in Transcript. In fact, I’ve already tipped by hand in the previous section when I sorted theList preparatory to displaying those that were selected while not showing those that were filtered out. If I simply sort theList and then call updateListField to re-display the list, this one should be a piece of cake. Or so it says here.

on mouseUp global theList set itemDelimiter to "|" if the label of me is "Select..." then exit mouse Up if the label of me is "Priority" then put 3 into itemToSort if the label of me is "Due Date" then put 6 into itemToSort if the label of me is "Status" then put 4 into it emToSort sort lines of theList by item itemToSort of each answer theList initializeListOrder updateListField end mouseUp

Yep, just works. This was the easiest part of the project. In some languages, it would be hard just to get the sorting algorithm to work; you’d have to write a bunch of comparison logic. Transcript “knows” you’ll want to sort containers, so it just provides an easy syntax for doing so. I love it!

(By the way, here’s an aside. Don’t tell anyone I admitted this, OK? It’s kind of embarrassing. The biggest mistake I made -- over and over again -- in building this application was forgetting to reset the itemDelimiter to the vertical pipe. When you don’t do that, Transcript gets all confused and produces absolutely unfathomable responses. If that happens to you, check to be sure you’ve handled this chore correctly.)

Hooking Up the Menus The final step in creating this application is to connect the menus so they carry out the desired functions when they’re selected. Recall that I defined the menus early in the design process because otherwise, squeezing them into available space later can cause problems with layouts.

As it turns out, I made a mistake in defining the menus earlier. It was an intentional mistake so I could demonstrate something that many Revolution developers overlook in their early use of the product. I have, on the ToDo menu, an item called “Add Note to Task” which, as the application takes final shape, is unnecessary. Editing a note allows me to add or edit the note attached to it. At the same time, I omitted a menu item for

Page 326: Software at Speed of Sound -- Dan Shafer

301

putting a task on hold even though I have a button for it and even though its opposite number, marking a task complete, does have a menu item.

So I open the Menu Builder from the Tool menu and I click on the “Edit...” button in the upper right area of the window. A list of menubars in the current project appears. I have only one, so I select it. Click on the ToDo menu and, as you can see in Figure A-21, I’m able to edit the menu I created earlier. This same technique will work for menubars you create outside Menu Builder for whatever bizarre reason you might want to subject yourself such pain.

Figure A-21. Menu Builder Showing Edits to ToDo Menu

I’ll want to click the “Auto Script” button again.

Page 327: Software at Speed of Sound -- Dan Shafer

302

Now, conveniently, Menu Builder includes an “Edit Script” button so I can just go in and modify the auto-generated scripts right here. No need to close the Menu Builder and go rummaging about looking for where I might have put the menubar. Pretty cool.

Given that I never want there to be only one way to accomplish anything, when it comes time to hook up menus, I’m generally just adding one-line handler and function calls to the menu scripts. That’s almost the case here, though I do have one or two items that might cause a bit more work.

Implementing Revolution’s Built-in Menus I’m going to start with the easy stuff. I click on the File menu, generate its Auto Script, then click on the Edit Script button. I select the commented-out line tells me to put my script, type “quit”, and apply the script. Revolutions’ built-in quit command is adequate for my needs. In a commercial application, I’d probably confirm the user’s desire to quit the program but those reminders just bug me, so I get to do it my way.

As it happens, Transcript also has built-in commands for cut, copy, paste, and clear. Frankly, given that I don’t have many large-scale editing tasks to do in this program, I don’t anticipate using these menu items very much if at all, but for the sake of completeness, I’ll include them.

Once again, I click on the Edit menu, generate its Auto Script, then click on the Edit Script button. Figure A-22 shows the script editing window before I make my changes. Again, I select the commented-out line and substitute the Transcript command. The result looks like Figure A-23.

Page 328: Software at Speed of Sound -- Dan Shafer

303

Figure A-22. Edit Menu Script Editor Before Changes

Page 329: Software at Speed of Sound -- Dan Shafer

304

Figure A-23. Edit Menu Script Editor After Changes

Scripting the ToDo Menu Before I open the script editor for my custom ToDo menu, I want to think a bit about how to implement the functionality it contains.

I have buttons corresponding to the functions each menu item carries out. So one way to handle the menus would be simply to have them send a mouseUp to the appropriate button and let the button script deal with the command. There’s nothing inherently wrong with that approach but it seems somehow tacky to me. It’s not very elegant.

What I probably should do is to extract the code from each of these buttons and create a stack handler with some appropriate name and then have the mouseUp handler and the menu handler call this new function. And that’s what I’m going to do.

I replace the contents of each of the mouseUp handlers with calls to the new handlers in the stack script called, respectively, markTaskComplete, markTaskToHold, deleteTask, and createTask. As I edit each button, I cut the contents of its script, replace it with the

Page 330: Software at Speed of Sound -- Dan Shafer

305

call to the handler, go to the stack script window, create a new handler and paste in the code.

(NOTE that if you use this technique elsewhere, the only thing to watch for is references to “me” or “my” which will shift when you cut and paste the scripts. Also, you don’t want to use this technique and then place the resulting handler lower in the message hierarchy. That’s almost never going to work.)

Now I can edit the ToDo menu script as shown in Figure A-24 and know that it will work as expected and maintain a certain amount of design cleanliness.

Figure A-24. ToDo Menu Script Editor After Changes

Scripting the Help Menu The Help menu has two items. One is for help and the other is an About box.

I’m going to have some fun with this one since it’s my application.

Page 331: Software at Speed of Sound -- Dan Shafer

306

If I ask for help on my own application, I deserve to be harrassed. So I’m going to put up a dialog box that says, “You need help with your own program? Get real!” (Try getting Microsoft to make dialog boxes with such snappy dialog! Harumph!)

But if I want to get some indication of what this application is about, I probably want to show off, so I’m going to create a new substack of this application called “About ToDoPlus” and open it when I (or one of my astounded friends) want to see who wrote this masterpiece!

First step, then, is to create the substack. Go crazy. Use wild colors. Animate an icon or 12. Or follow my lead and simply create a boring old dialog box with basic information in it.

Figure A-25 shows the About stack I created. I saved it as “AboutToDoPlus,” a substack of the main ToDoPlusMain stack.

Figure A-25. About Box for ToDoPlus Application

That’s All, So What’s Next? Well, that wraps things up for this volume of Programming at the Speed of Thought. We’ve covered a lot of ground., hopefully had a good bit of fun, and learned a lot along the way.

I’ve got a constantly evolving, Open Source version of this ToDoPlus application on my RevolutionPros.com Web site at http://www.revolutionpros.com. And there are two more volumes in this book series. Join my members-only RevolutionPros.com

Page 332: Software at Speed of Sound -- Dan Shafer

307

community and you’ll get early sneak peaks at forthcoming chapters, a chance to help improve the books before they’re published, discounts on all things Revolutionary from my site, and a lot more cool stuff.

Vive la revolution!