Everything You Always Wanted to Know About Writing JAWS Scripts, But Didn’t Know Whom to Ask By Kenneth A. Gould Copyright 2000 by Freedom Scientific Inc., All Rights Reserved Preface The Microsoft Word version of this manual has been set up so you are able to jump directly to any section shown in the Table of Contents. To do this, you can click anywhere on the appropriate line in the Table of Contents, either on the entry itself or on the page number to the right of each entry. One way to accomplish this with the keyboard is as follows: Find the section of interest in the Table of Contents by moving there with the PC cursor. Route the JAWS cursor to the PC cursor. Click the left mouse button by pressing NUM PAD SLASH. You will then jump automatically to the page of interest. This manual also contains hyperlinked cross-references to sections that are in the form See Section X, where X is the hyperlinked section number. You can jump directly to the section by clicking on the section number reference with the JAWS cursor. After reading the section you have jumped to, you can return to your original position in the Table of Contents or to your original cross-reference at any time by pressing ALT+LEFT ARROW. This manual has been updated for version 3.7 of JAWS. Table of Contents 1 Introduction 7 1.1 What are Scripts? 7 1.2 Scripts versus Functions 8 1.3 Constants and Variables 10 1.4 Structure of Manual 12 Part I: Getting Started With Scripts 2 Scripts: A Brief Overview 15 2.1 What are Script Files? 15 2.2 What is the Script Manager? 18 2.3 Anatomy of a Script 19 3 Our First Scripts 21 4 The Script Manager 27 4.1 The Menus 27 4.2 Script Manager File Types 27 4.3 Include Statements 28 4.4 The New Script and Script Information Dialogs 29 4.4.1 General Tab 29 4.4.2 Parameters Tab 31 4.5 The Insert Function and Insert PerformScript Dialogs 32 5 Creating Simple Scripts 35 5.1 Script Documentation 35 5.2 Individual Script Structure 35 5.3 Using Script Functions 36 5.3.1 Reading Text 36 5.3.2 Moving Around on the Screen 38 5.3.3 Creating Reading Scripts 42 5.4 Putting It All Together 44 Part II: Creating Advanced Script Files 6 Windows Program Structure 50 6.1 Hierarchy 50 6.2 Identifying Windows 52 6.2.1 Window Classes, Types, Type Codes, and Subtype Codes 52 6.2.2 Control ID's and Window Handles 54 7 Building Blocks of the Script Language 57 7.1 The Script 57 7.2 The Function 58 7.3 Types of Statements 59 7.3.1 Comments 59 7.3.2 Includes 60 7.3.3 Variables 61 7.3.4 Constants 65 7.3.5 Built-in Functions and Operators 65 7.3.5.1 Built-In Functions 66 7.3.5.2 The GetCurrentWindow and GetFocus Functions 67 7.3.5.3 The Pause and Delay Functions 69 7.3.5.4 A Word About SDM Windows 69 7.3.5.5 Arithmetic Operators 71 7.3.5.6 Logical Operators 71 7.3.5.7 Bitwise operators 73 7.3.5.8 Hook Functions 74 7.3.5.9 Introduction to Script Writing With Microsoft Objects 77 7.4 Controlling Flow 85 7.4.1 Sequential 86 7.4.2 Selection (Conditional) 86 7.4.3 Iterative 89 7.5 User-Defined Functions 91 7.5.1 Simple Functions 91 7.5.2 Functions That Require Parameters 94 7.5.3 Functions That Provide Returns 98 7.5.4 Event Functions 100 7.5.4.1 List of Event Functions 105 8 Script Writing Techniques 111 8.1 Exploring the Application With the Utility Functions 111 8.2 Obtaining Window Information With the ScreenSensitiveHelpTechnical Script 114 8.3 Script File Types 114 8.3.1 Source Files 114 8.3.2 Compiled Files 116 8.3.3 Include Files 116 8.4 Using Variables 117 8.4.1 Naming Conventions 117 8.4.2 Declaration Placement 118 8.4.3 Using Global Variables 118 8.5 Using Constants 119 8.6 Using Multiple Functions 123 8.6.1 Using Functions Sequentially 123 8.6.2 Using Nested Functions 124 8.7 Selecting and Manipulating Cursors 125 8.8 Sending a Keystroke 129 8.9 Making Your Scripts Compatible With Custom Verbosity Levels 129 8.10 Synchronizing Documentation 130 9 Debugging 132 9.1 The JAWS Script Compiler 132 9.2 Erroneous Activity 133 10 Strategies for Attacking New Applications 136 11 Guidelines for Creating Distribution Script Files 137 12 Converting Macro Files 138 13 Acknowledgments 140 14 Appendix A - Answers to Homework Assignments 141 14.1 Answer to Homework Assignment #1 141 14.2 Answer to Homework Assignment #2 141 14.3 Answer to Homework Assignment #3 142 14.4 Answer to homework Assignment #4 143 14.5 Answer to Homework Assignment #5 143 14.6 Answer to Homework Assignment #6 144 14.7 Answer to Homework Assignment #7 146 14.8 Answer to Homework Assignment #8 146 14.9 Answer to Homework Assignment #9 147 14.10 Answer to Homework Assignment #10 147 15 Appendix B - Description of the Script Manager Menus 150 15.1 File Menu 150 15.2 Edit Menu 150 15.3 Script Menu 151 15.4 View Menu 151 15.5 Window Menu 151 15.6 Help Menu 152 16 Appendix C - The Most Important Built-In Functions 153 17 Appendix D - Important Built-In Functions Arranged by Task 181 17.1 Cursor 181 17.2 Positioning 182 17.3 Conditional Processing and Looping 183 17.4 Say 183 17.5 String and Integer Manipulation 184 17.6 Application 185 17.7 Braille 185 17.8 Mouse 186 17.9 Find 186 17.10 Windows and Objects 186 17.11 Frames 188 17.12 Scripts 189 17.13 SDM 189 17.14 Speaking Level 189 17.15 Options 190 17.16 System Activities 190 17.17 Colors 191 17.18 Control Synthesizer 191 18 Appendix E - How to Scope Out and Customize an Unknown Application 193 Introduction What are Scripts? First of all, let’s start out with some definitions. What is a script anyway? When you perform a job on a computer, it usually involves a number of steps. You may have to press several keys to get you to the right part of a program to do a job, or you may even have to press several keys to open the program you want to use. Then, once you are where you want to be, you may have to press several more keys to enter the data you want. For example, you might be in a word processor and want to type your return address at the top of a letter. This could become a tedious task if you have to do it a lot, so wouldn’t it be nice if you could have the entire return address typed out for you with one keystroke? That is exactly the type of thing a script can do. A script is a sort of mini computer program which combines many steps or keystrokes into one operation that you can activate quickly and simply. Well, at least that’s one type of script. JAWS and other computer applications use scripts all of the time, and obviously they aren’t always being used to type out return addresses. So, more globally, scripts are sequences of individual steps that can be used to activate and control a wide variety of computer processes from things as simple as entering repetitive strings of data to many of the things your computer does on an ongoing basis as part of its operation. It is this sort of function we are going to be concerned with in this manual. While you can certainly use what you will learn here to create a return address script, you will also learn how to control JAWS in ways which will make it behave more like you would wish. Many aspects of computer control which we take for granted are actually under the control of scripts. We don’t actually think of them as scripts since they are coded as part of the application’s internal programming. For example, when you use cursor movement keys or call up a dialog box in Microsoft Word with a hot key, you are actually invoking a part of the program which performs a series of operations or steps. By our definition, this is a script. JAWS uses many different kinds of scripts which tell it what to do in certain situations or when you hit certain keys. What makes JAWS such an immensely powerful program is that the designers have decided not to hide away those scripts as part of the main program and, thus, make them inaccessible to the user. If they had done this, then JAWS would be a far less flexible tool and would be much less adaptable to the needs of unusual applications. JAWS’ designers made the conscious decision to let you write your own scripts for a particular purpose and to let you see and modify the individual default scripts that control many aspects of the program's performance. The modified or newly-written scripts are then organized into individual script files which have the same name as (but a different extension from) the application for which they were created. Access to the scripts controlling JAWS' most fundamental behavior has been provided for two very good reasons. First, every user wants to be able to personalize the program to behave just as he or she would like. Second, while the JAWS designers have anticipated many of the screen reading situations which will occur with different applications, it is impossible to foresee every weird or nonstandard screen setup which the world’s programmers will come up with. You have at your disposal a powerful arsenal of script commands which you can apply to the conquest of these unusually-designed applications. All you have to do is learn how to use them. By the way, it is not always necessary to use a script to customize JAWS. For example, one can cause certain parts of the screen to speak or be silent by using the frames which can be created with the Frame Manager. As time goes by, there will surely be more features such as this added to JAWS that will allow you to do more customization without resorting to scripts. But there will always be situations too bizarre or convoluted for anything but a well-written script to cope with. Remember that after you have learned how to use the JAWS script language you have a very powerful tool that is much more incisive than any simple feature can be. So, you’re probably asking yourself now, what’s the difference between a macro and a script? Well, in a word, there really isn’t any difference. Scripts are macros, and that’s all there is to it. Don’t let the jargon confuse you. We’ll call them scripts from now on, but “A rose by any other name," etc., etc. Scripts versus Functions JAWS scripts can be divided into two main categories. Those which are activated by a keystroke are called scripts, and those which are not are called functions. Let’s first talk about the keystroke scripts. How does JAWS know that it should read the new line of text when you are in a word processor and hit the DOWN ARROW key? Well, it knows this because there is a script in the main JAWS script file (called the Default script file) which is tied or “bound” to the DOWN ARROW key and tells JAWS to read the new line. This particular script has the name “SayNextLine” and it is activated when you hit the DOWN ARROW key. This script is fairly complicated because it has to analyze where you are and what you are doing when you hit the DOWN ARROW key. It will check to see what kind of window you are in when you hit the DOWN ARROW key, and, if you are in a word processor text window, it will speak the line to which you move. There is a line in this script file that reads “SayLine ()” and it gets activated after your cursor moves on to the next line. This is the line of the script which actually performs the function which reads the line. There is a similar script called “SayPriorLine” which gets activated when you hit the UP ARROW key. This script uses the same “SayLine” function as part of its coding. When you hit the RIGHT and LEFT ARROW keys, two other scripts get activated, “SayNextCharacter” and “SayPriorCharacter”. These scripts use a function called “SayCharacter” as part of their coding to speak the new character after your cursor moves on to it. There are other key-bound scripts designed to operate when many of the other keys on your keyboard are pressed. These scripts analyze the current situation and try to perform an action appropriate for that situation. If the situation is very unusual and has not been anticipated by the JAWS designers, then that is when you may have to do some modifications to make it perform as required. The scripts that are not bound to or activated by keystrokes are called functions. Since these functions cannot be activated by a keystroke, they must be utilized in other ways. Functions also differ from key-bound scripts in that they return some information after they finish executing, whereas key-bound scripts never do. Once again, don’t let the jargon snow you. A function is just a script that is not tied to a particular keystroke and which returns some information when it is done executing. Why is this done? There are two very good reasons. First, some functions operate automatically when certain events occur. There is, therefore, no need to have such functions tied to keystrokes because it is not a keystroke which activates them. Second, sometimes a set of scripting statements is very generally applicable to many situations and can be used over and over again. Instead of writing out this set of statements for every script that needs it, it can be placed into a function which can be utilized or "called" by any other script that needs it. Thus, functions are subdivided into two categories, the ones that trigger automatically when certain things happen and those which only get triggered when they are “called” by another script. The first type is called an event function because it runs automatically when certain system events happen. For example, the NewTextEvent function gets called when any new text gets written to the screen, and the BottomEdgeEvent function gets called when the cursor reaches the bottom of a window. Without the NewTextEvent function, nothing at all would be spoken automatically by JAWS when information appeared on your screen, and without the BottomEdgeEvent function, your PC would neither beep nor speak a warning message when the JAWS cursor reached the bottom of a window. The second type of function, the type not called automatically by certain Windows situations, has no special name (believe it or not) so we’ll just call it a function. These plain functions operate only when they are called by another script or function. This is done by placing the name of the function within another script or function. If you do this, then when the script gets to the line which has that function’s name, it will cause that function to be run. This process is termed “calling” a function. Yeah, I know, more jargon, but you’ll just have to get used to it. Calling a function means, quite simply, placing that function’s name into another script so it will get performed at the appropriate time. For example, the NewTextEvent function mentioned above has a line in it that reads “SayNonHighlightedText ()”. If the NewTextEvent function has analyzed the new text written to the screen and decided that it is properly handled by the SayNonHighlightedText function, it will pass off the job to that function by calling it and then let it speak the text for you. On the other hand, if the NewTextEvent function decides that the screen information is highlighted text, then it will call a different function named “SayHighlightedText” instead, and it will let that function do the speaking for you. Decisions, decisions, decisions. Well, that’s what scripts do. They analyze what’s going on and make a decision about how to handle the screen reading for you. If they don’t do it right, your job is to modify the relevant script so it does do it right. By the way, you might have noticed that there was a left and right parenthesis after the function name SayNonHighlightedText mentioned above. These parentheses are always required when calling a function within a script. Sometimes information is placed between these parentheses, but that’s something we’ll talk about later. (See Chapter 7.5.2.) The plain-Jane, non-event functions are further divided into two types. The first type is the list of over two hundred functions which are available for you to use in the JAWS script language. These functions are called "built-in" functions and are hard-coded into JAWS. You can call them when creating scripts, but you cannot change them. The second type of plain function is the type present in the script files, either created by JAWS’ designers or added by a user. These are called user-defined functions. After these functions have been created, they will also appear in the function list for you to use within the scripts you create. Obviously, the main difference between these and the hard-coded functions is that these can be modified by a user, if necessary. Constants and Variables We need to introduce two other terms before actually getting into script writing. They are “variable” and “constant”. A variable is a word of one or more letters which can be used to store a value that is determined during the processing of scripts. This value is often not known in advance and can change one or more times during script processing. The value that is determined is assigned to the variable during the course of the script’s operation, and the variable maintains that value until and unless it is changed by another script operation. The value of this variable can be used to make decisions by that script or another script. For example, in the statement Let X = 1, X is the variable, and we are assigning a value of 1 to it. X has the value of 1 until and unless we change it to something else or the operation of the script changes it for us. This is, by the way, the type of script statement we use to assign a value to a variable manually. In the course of running a particular script, we can have statements that do one thing if X equals 1, but do something else if X has a different value. Further, the value being held by X might represent the kind of window we are currently in, say a button, an edit field, or a list box. X would have a different value for each of these types of windows, and our script might need to perform different operations depending on what type of window we were in and, therefore, what value was assigned to X. If our script had the capability of looking into the Windows operating system and determining what type of window we were in, the script could then assign the appropriate value to X and the script could then continue its processing, deciding which of several courses of action to perform based on the value (i.e., type of window) being stored in the variable. JAWS does have this capability to look into the Windows operating system in this fashion, and this type of decision based on the current window type is exactly the kind of decision-making continually being performed by JAWS. The next time that same script runs, we may be in a different window, and the variable will, therefore, have a different value stored in it. The script would then decide to do something quite different, the action appropriate for that type of window. Again, scripts often must make decisions, and the value stored in a variable is one way they can decide what to do next. Constants are quite a different matter because, as their name implies, they do not change their value. Once a value is assigned to a constant, it stays that way. This may seem to be a rather strange waste of time, but it is really quite convenient. Constants are a way of using easily-remembered names to store hard-to-remember strings of letters or numbers. For example, let’s say our friend Joe has a social security number 589-43-3894. Further, let’s say we have to work with ten people, each with his or her own social security number. We could make a constant called JoeSocSecNum and then place Joe’s social security number in that constant by using the equation JoeSocSecNum = 589433894. We could do something similar with each of the other persons and his or her social security number. Then, when we wanted to print out a person's social security number, all we would have to do is ask the program to print out the value of each person’s social security number constant. Since JoeSocSecNum is much easier to remember than 589-43-3894, we have made life much easier on ourselves by using a constant instead of trying to remember a long number. JAWS does this sort of thing a lot, and that’s what we mean when we talk about constants in the context of the JAWS scripting language. In case you are still a little confused about variables and constants, let’s use another analogy that might help clarify them a little more. We’re all familiar with touch-tone telephones that use a numeric keypad of buttons that we press to dial a telephone number. In addition to the numbers zero through nine and a couple of symbols, some telephones have some additional programmable buttons that can be used to store whole telephone numbers that we call frequently. One might, for example, store the sales number of his favorite software vendor in one of those buttons so he could quickly call to place an order without having to look up the number. Now, the buttons that contain the numbers zero through nine could be considered as constants since the numbers they contain never change. The first button on the top row is always a one, so it is a constant. The programmable buttons, however, can be changed at will to contain new numbers. You might, for example, have to change the software vendor button to a new phone number if the company moved to a new address. Since these programmable buttons can be changed at will, they are much more like variables, and they can be altered to contain the information we most need at any given time. Variables in scripts are like that, too. They can be changed as needed, either by the user or automatically by a script, depending upon circumstances. Structure of Manual As we mentioned above, individual JAWS scripts are arranged in files called script files. This manual contains all of the information you will need to write your own scripts and arrange them into files that will be used by your computer every time you run an application. The following is a list of the remaining chapters of this manual with a brief description of what each contains. Part I: Getting Started With Scripts Chapter 2 - “Scripts: A Brief Overview” provides a broad, general understanding of how scripts and the JAWS Script Manager function. The difference between the default and application script files and how they work together is also covered. “Anatomy of a Script” gives a brief introduction to the form a script takes so you can better understand the construction of “our First Scripts” in the next chapter. Chapter 3 - “Our First Scripts” helps you to create two simple scripts, compile them, and load them so that you can gain a little confidence. Chapter 4 - “The Script Manager” provides an in depth tour of the Script Manager and all of its tools for creating scripts and script files. Chapter 5 - "Creating Simple Scripts" teaches script writing on a small scale. It is intended for those who want to tweak the existing scripts and create scripts to read textual information from the screen. Part II: Creating Advanced Script Files Chapter 6 - "Windows Program Structure" covers the various identifiers used to classify and differentiate among the many windows found in Windows programs. It also covers how windows are arranged in a hierarchical order. Chapter 7 - "Building Blocks of the Script Language" provides details on the individual constructs that make up scripts. This is where we start to put things together that will become scripts. We’ll also look at when and how to use the various functions. Chapter 8 - "Script Writing Techniques" starts putting all this information to use. It provides the techniques used in writing advanced scripts. Chapter 9 - "Debugging" covers how to work out the bugs in your scripts along with some tips that help with the process. Chapter 10 - "Strategies for Attacking New Applications" gives ideas on how to approach the problem of analyzing and scripting a new application. Chapter 11 - "Guidelines for Creating Distribution Script Files" lists several recommended rules to observe for those creating script files. Chapter 12 - "Converting Macro Files" describes the process of converting macro files written for versions of JAWS for Windows prior to version 3into script files that work with the current version. Part I: Getting Started With Scripts Scripts: A Brief Overview Suppose you are using an application program that provides a description of each menu item at the bottom of its window. A sighted user would just glance down to see that information whenever the purpose of that particular item was unclear. Sure, you could turn on the JAWS Cursor, go to the bottom of the window and read the line, but wouldn’t it be easier if you could just press a hot key to read the help text? Or even better, how about if JAWS could read it automatically? And, of course, you’d want to be able to turn that automatic reading on and off. Well, if this application you are using had a script file written for it, and if this script file contained a script to do these things for you, you would then be able to have this sort of convenient access. If no one else had created this special script for you, you could then create it yourself, provided you have learned how to use the JAWS script language to build your own scripts. This improved access would help to bring the efficiency of using this program up to that of a sighted person, and it would improve your productivity. What are Script Files? JAWS is always under the control of scripts. As we discussed above, a script is a small computer program that modifies the way JAWS performs for a specific situation. JAWS script files are collections of individual scripts which are loaded specifically for use with a given Windows application. These script files are loaded automatically by JAWS whenever you enter a new application. It is important to understand that there are two types of script files: default and application . You can think of script files as “stacked” in a sort of pile, with the default on the bottom of the stack. The default script file is loaded when JAWS starts and is always active in all sessions. An application script file is stacked on top of the default when its application loads. JAWS knows to load the application script file on top of the stack because the main part of the application script file name (i.e., the part without the extension) is the same as the application program name. Thus, our default script file is named DEFAULT.JSS, and an application named SPREADSHEET.EXE would have a script file called SPREADSHEET.JSS. When you leave this particular application, the specific script file it uses will be unloaded, and all of the default scripts will again be active until another application’s script file is loaded. The default script file that is loaded when you first start JAWS contains hundreds of scripts. These scripts give JAWS all of the information it needs to provide proper voice output in most situations. This file tells JAWS what to speak and when to speak it in most circumstances when running well-behaved applications. In many applications, however, there are situations which deviate from the normal. There may also be particular instances when customized reading of the screen is required. If these occur it may be necessary to create a script file just for this application. If such a script file has been created and exists in the JAWS subdirectory which contains the JAWS configuration files, then it will also be loaded when that application is run. This script file is the first one that is looked at when a particular keystroke is used or when a situation occurs which needs to call one of the event functions described earlier. (See Chapter 1.2.) If the application script file does not contain a script relevant to the situation, then JAWS will look in the default file. If the script is found in one of these two places, it will be executed as soon as it is found, and no further processing is required. IF no script is found in either location, then JAWS will pass the keystroke off to the application, and that keystroke will be performed just as if JAWS were not running. This hierarchy is very important. If a particular script which exists in the default file is not appropriate for the application being used, the user can place a customized version of that script in the application’s script file, and that version will be used in preference to the one present in the default file. Since scripts higher up in the stack take precedence, a script in the application script file will be run instead of one in the default script file if they are both bound to the same keystroke, and a function in the application script file will be called in preference to one in the default script file if they both have the same name. For example, that means if you have a script in the application script file assigned to CTRL+G, and you also have a script in the default script file assigned to CTRL+G, only the one in the application will be executed when you press CTRL+G. It doesn’t even matter if the two scripts have different names. It is the keystroke binding which determines which script will run. If you attempt to assign a script to a keystroke which is already in use by the default file while using the JAWS Script Manager (See Chapter 2.2 and 4.), you will be given a warning message, and you will have the opportunity to continue with the assignment or change it to another key. This is to prevent you from accidentally disabling a script in the default file without realizing you are doing so. There are some important considerations when deciding on the names and keystroke assignments for your scripts. If, on the one hand, the user gives the customized script a different name from the one in the default script file, then the user can also assign the script in the application’s script file to the same keystroke it is assigned to in the default file with no adverse consequences. If, on the other hand, the user gives the application script the same name that it has in the default file, it is not even necessary to assign a keystroke to that application script. In either case, JAWS will execute the version present in the application’s script file and will ignore the one present in the default file. This is because JAWS looks in both the default keymap file, DEFAULT.JKM, and the application keymap file, APPLICATION.JKM, to find out if a script with a particular name is assigned to a key when that key is pressed. JAWS will always execute a script in the application script file in preference to one in the default script file, even if the key assignment is made in the default keymap file. In other words, if both script files have a script with the same name and the key assignment is made in the default script file, JAWS will still know that it's supposed to execute the one in the application script file. Functions, since they are not bound to keystrokes, must use a different mechanism. If the script is a function and, thus, is not bound to a keystroke, then a function with a particular name in the application script file will be run instead of such a function in the default script file that has the same name. So in the case of functions, it is the name that decides the outcome, not the keystroke. Note that there is a special case which occurs when you have a script in the default script file and one in the application script file which both have the same name and which are not bound to a keystroke. In this case, JAWS uses the script name to determine which is active, and the script in the application script file will take precedence over the one with the same name in the default script file. In this special case, therefore, scripts not bound to keystrokes determine precedence in the same way that functions do. Thus, if a script is run using a PerformScript statement, the script in the application script file will be utilized in preference to the one in the default script file. PerformScript statements are an alternative way of running scripts, and they will be discussed later in this manual. (See Chapter 4.5.) It was mentioned above that, if there is an application script file available for a particular application, it will be loaded automatically when that application is loaded. JAWS knows to do this because the main part of the script file name is the same as the name of the application. Starting with version 3.3 of JAWS for Windows, an additional capability was added which allows a user to load a script file of his or her choice at any time. This script file can have any name, and that name does not have to resemble the name of the current application. If such a script file is loaded, it will replace the regular application script file in the stack if such a file is present. This special script file will, in turn, be replaced automatically any time the user switches to a new application that loads its own script file. This new capability allows a user to load a script file that is tailor made to cope with unusual situations in an application where the requirements are very different from the rest of the application. The special script file could be loaded manually with a keystroke or automatically when certain windows or items appear on the screen. The function used to load special script files is called SwitchToScriptFile. It can be called manually or automatically by techniques which will be described in the remainder of this manual. Finally, it is important to understand that scripts present in the default script file are available from any application being run, unless, as discussed above, the default script is superceded by a script in the application script file. So if you have a script which you wish to use from more than one application, you may wish to place it in the default script file rather than in a particular application script file. What is the Script Manager? JAWS provides you with all the tools you need to modify and create scripts. These tools are present in a program called the Script Manager. The Script Manager is a full-featured, text editor program. In addition to the usual features contained in most text editors, it has built in capabilities to help you create scripts, insert functions into your scripts, display reference information about these functions and scripts that have already been written, check for errors that violate the rules of correct script writing, and save and compile your script file. In addition, there are three hot keys which allow you to look through the scripts in your script file more efficiently. The function key, F2, can be used to jump forward from script to script, while SHIFT+F2 can be used to jump backwards from script to script. The hot key combination CTRL+L can be used to bring up a dialog which lists all scripts in the current file in alphabetical order in a separate window. This makes it easier to find a particular script within a large file if you know its name but do not remember where in the file it resides. We said in the last paragraph that the Script Manager could be used to save and compile your script files. What do we mean by the word “Compile”? When you write scripts for JAWS in the Script Manager, you write them in plain text. JAWS, however, cannot use them in this form since it cannot understand plain text. When you save your script file in the Script Manager, it automatically performs an additional step which other text editors and word processors do not. It creates an additional file which has been converted from text into a binary format which JAWS can understand and use. The original text file is saved with a JSS (JAWS Script Source) file extension, and the compiled or binary version is saved with a JSB (JAWS Script Binary) file extension. This is why it is important always to create and save your script files using the Script Manager. If you edit and save your files using some other text editor such as Notepad, they will not be compiled when you save them, and JAWS will be totally unaware of the changes you have made. There are three ways to start the Script Manager. These are (1) from an application by pressing INSERT+0, (2) pressing INSERT+F2 to invoke the Run JAWS Manager Dialog, followed by the letter S and the ENTER key, or (3) by choosing Script Manager from the JAWS Utilities menu. When you start the Script Manager from within an application with either method 1 or 2, it automatically opens the script file for that application. If no file exists, the Script Manager creates one. When you start the Script Manager from the Utilities menu, it does not open any file. The Script manager will be discussed in much more detail below in the chapter titled "The Script Manager". Anatomy of a Script Scripts and functions can be divided into three main sections. The first line starts the script or function and is of the format of one of the following two lines: Script ScriptName () Function FunctionName () If the script is bound to a keystroke, its first line will look like the first line above. If it is a function and, thus, is not bound to a keystroke, it will look like the second line. Sometimes there is an additional word before the word Function, but that will be covered later. (See Chapter 7.5.3.) In either case, the second word in the line will be the script or function name. This can be anything you want, but it is very helpful to make the name descriptive of what the script or function does. If you use several words clumped together, capitalize the first letter of each word so JAWS can pronounce it properly. Spaces are not allowed in a script name. Some typical examples of script names are SayNextLine, SayPriorCharacter, SayWindowTitle, and NextDocumentWindow. Note that it is very easy to tell from these titles what the scripts are intended to do. Finally, there will be a left and right parenthesis at the end of the line. Sometimes these parentheses will contain some information, but that will be discussed later. (See Chapter 7.5.2.) Next comes the body of the script. This consists of all the instructions you add during the script writing, such as variable declarations, comments, flow control statements, function calls, PerformScript statements, and arithmetic operations. You will learn what all of these types of instructions are as we proceed through the manual. For immediate information on the most important of these, See Chapter 7.3. Finally, the last line of the script will look like one of the next two lines: EndScript EndFunction Obviously, the first line is used to end a script, and the second is used to end a function. When you use the script creation dialog of the Script Manager, as discussed below, a blank script will be created with the first and last lines already present on the screen with several blank lines in between. All you have to do is fill in the body of the script between these first and last lines. Our First Scripts Now let’s write a couple of small scripts. The first will have JAWS speak our name on demand. We'll place it in the Notepad script file so that we can learn a little about script writing without messing around in the very important default script file. Follow these steps exactly and you will see that writing simple scripts is not all that hard. If you do not already have JAWS for Windows running, start it. Open Notepad from the Start Menu/Programs/Accessories group. Choose Script Manager by pressing INSERT+0 or by pressing INSERT+F2 followed by S and ENTER. JAWS will tell you that you are in NOTEPAD.JSS, the script source file for Notepad. You will note that there are no scripts in this file. Notepad needs no scripts, so opening the Script Manager from within Notepad started a brand new source file. Choose New Script from the Script menu or use the accelerator key combination, CTRL+E. The New Script dialog appears so we can name our script and write its documentation. For now, just follow the steps. We’ll cover naming and documenting scripts in more detail later. (See Chapters 4.4 and 5.1.) When the New Script dialog appears, the cursor is in the Script Name field. Type in SayName. Be sure to capitalize both the S and N, and don’t put in a space. Press TAB to move to the Can be attached to key checkbox, and press SPACEBAR to check it. This is the step which tells the Script Manager that this is a script and not a function. By checking this box, we make it possible to attach the script to a key. Press TAB to move to the Synopsis field, and type in the words, “Say our Name.” Press TAB to move to the Description field, and type in the words, “The name of the person who wrote this script.” Press TAB to move to the Category field, and type in the word “Test.” The Category field is an edit combo, so you can pick a category by typing one in or by arrowing down through the list to find one that is appropriate. Press TAB to move to the Assign To field, and press CTRL+SHIFT+N to assign this keystroke to our script. We won’t need any other fields filled in for our script, so press ENTER to close the New Script dialog and insert our blank script into the editing area. We are now back in the main text area of the Script Manager, and we have a blank script where the first line says “Script SayName ()” and the last line says “EndScript.” We have been placed between these two lines. Arrow up to the first blank line after the first line, and you are ready to start writing the body of the script. We want JAWS to speak a string of characters, that is, our name. We can do this by using one of JAWS built-in functions called SayString. Remember that JAWS’ built-in functions are functions that are hard-coded into JAWS to do particular jobs and cannot be modified by the user. These are the basic building blocks we use to create scripts. This one is designed to speak messages during script execution. Note that there is no space in the function name. Computer functions are often named by putting a couple of words together that describe the function. What we then need to do is tell the SayString function what we want it to speak. In other words, the SayString function tells JAWS we want it to say something, and then we have to tell the function what words we want it to say. We have to pass some information to the function for it to do its job. This information we are passing to the function is called a parameter, and that’s one more piece of jargon for you to remember. In this case, the parameter we want to pass is the string of letters which comprises our name. Type your code line exactly as it appears on the next line except that you should substitute your own name for the letters XXX: SayString (“My name is XXX”) You should now have the following three lines on your page exactly as they appear below. Remember that close is not good enough as computers expect us to be precise and perfect. Also, a few extra blank lines are not important. JAWS ignores blank lines in scripts. Script SayName() SayString (“My name is XXX”) EndScript Now we need to save and compile the Notepad script file. Press CTRL+S and you should hear, "Compile Complete." If not, go back and retrace the steps and try again. Close the Script Manager. This should place you back in Notepad. Test your work by pressing CTRL+SHIFT+N. How about that! Your first script. Switch to another application and try the same hot key combination. Note that this time the script does not work. This is because we placed the script in the Notepad script file, and that file was unloaded when we left Notepad. If we had placed the script in the default file, it would have worked in any application unless that application's script file also had a script assigned to the same hot key. Let's switch back to Notepad and try something else. Press the INSERT+1 key combination (using the 1 on the number row, not the keypad), and JAWS will say "keyboard help on." This is the help mode you can use to explore the keyboard without actually activating any of the keys. (You can leave the keyboard help mode by pressing INSERT+1 a second time.) Now press your hot key again. Wow! This time the hot key doesn't say your name, it says what the hot key does. Press the hot key combination twice very quickly, and you'll hear even more detailed information about what the hot key does. These pieces of information are the synopsis and description fields you filled out while creating the script. Now you can see why it is important to fill these fields out correctly. For our next script, let's do something a little more practical. Open the WordPad application from the Start Menu/Programs/Accessories group. Now drop down the file menu by pressing ALT+F. Press the DOWN ARROW seven times, and you will land on a line that speaks the name of the last file you opened in WordPad. (If you've never opened any files in WordPad, do so now so you will have this line in your file menu.) Wouldn't it be nice if we could read this line and know what the most recently opened file was without using all of those keystrokes? Let's write a script to do just that. As before, follow the steps as described below. Open the Script Manager by pressing INSERT+0, or INSERT+F2 followed by S and ENTER. As before, you should be placed in a blank script file named WORDPAD.JSS. Start a new script by selecting New Script from the script menu or by pressing the CTRL+E accelerator key combination. You'll be placed in the Script Name field. Type in LastOpenedFile without any spaces and capitalized as shown. TAB to the Can Be Attached to Key check box and check it. TAB to the Synopsis field and type in "Says last opened file." (Do not include the quotation marks, they are only used here to show you what to type. This comment applies to all quotation marks used to tell you what to type.) TAB to the Description field and type in "Says the name of the last file opened in WordPad." TAB to the Category edit combo and arrow down until you reach the Say category. Tab over to the Assign to Hot Key field and press CTRL+SHIFT+L to make this your hot key. TAB over to OK, and press ENTER. We are now back in the main text area of the Script Manager, and we have a blank script where the first line says “Script LastOpenedFile ()” and the last line says “EndScript”. We have been placed between these two lines. Arrow up to the first blank line after the first line, and you are ready to start writing the body of the script. This script is quite a bit more complicated than the last one. Shown below is the completed script, including the first and last lines that were placed in the blank source file by the Script Manager. Type the second through the next to last lines into your blank script, and you should end up with something that looks just like what is shown below Script LastFile () SpeechOff () {Alt+F} Pause () NextLine () NextLine () NextLine () NextLine () NextLine () NextLine () NextLine () SpeechOn () SayLine () SpeechOff () {escape} {escape} Pause () SpeechOn () EndScript So, what in the world are all of these statements doing? Well, you may have noted that when you pressed ALT+F in WordPad to open the file menu, JAWS said "menu active, file, new, CTRL+N." We don't want to hear all that stuff during our script execution, so the second line in the script, SpeechOff () turns off speech until we're ready for it. Then the statement {ALT+F} sends the ALT+F command to WordPad, just as if we'd typed it in from the keyboard. As you'll learn later (See Chapter 8.8.), we always place keystrokes we want to send to the application between left and right curly brackets or braces. Then we have a pause statement that gives the application a chance to drop down the menu before the script proceeds. This is done a lot when asking applications to perform activities. Without this, our script might get ahead of the application and do things before the application is ready. Then we arrow down seven times with the seven NextLine statements. This brings us to the line where the last opened file is listed. Before we can speak this line, we must turn the synthesizer back on with the SpeechOn statement. Next we have a SayLine statement which speaks the contents of the current line. That's the actual function which gives us the data we want. Now we have to turn the speech back off with the SpeechOff function so we don't hear all the stuff JAWS normally speaks when exiting a menu. Now two Escape statements will get us out of the menu. Note that here, again, these must be between curly brackets. Then we have another Pause statement to allow the menu time to disappear, and finally we finish by turning the speech back on with a final SpeechOn statement. Now you should save and compile your script by pressing CTRL+S. Return to WordPad, and press your new hot key, CTRL+SHIFT+L. You should be rewarded by hearing JAWS speak the name of the last file you opened. If you think this is great, just wait. You are just beginning to experience the kinds of things you can do with this fabulous scripting language. No longer will you be at the mercy of some dumb programmer who didn't follow normal rules and write the application so it could be easily accessed with speech. No longer will you be forced to execute a dozen keystrokes to get a piece of information off the screen that your sighted friends can access just by glancing at the screen. No longer will you have to wait for someone else to configure a new application for you. It may take some work to learn this language, but you will be rewarded with the capability of making your applications speak and behave much more like you wish. Learn this stuff well, and you will be able to access your applications much better and more efficiently than you would ever have imagined could be possible. One more word of encouragement is necessary before getting to the real nitty gritty of the scripting language. Sometimes it's daunting to see a completed script such as the one above that works just the way it should. The new script writer looks at such a script and says "How in the world did they figure out which statements to use and the correct order to put them in?" It must be understood that scripts do not pop out of thin air as a correctly and completely-finished product. The author tried many different configurations of the above script before coming to the one that worked best, the one shown above. The first version did not contain any SpeechOff or SpeechOn statements, and the resulting script spoke much too much information. So these statements were added. But several trials were necessary before the best placement was found. Then the script still did not work correctly until Pause statements were put in the right places, a process that required more trial and error. The point is that you will have to make your best guess and try something. If that doesn't work, try something else. As you gain more experience, you will get it right more quickly. But the only way to learn this new language is by the trial and error process which occurs as you make your scripting attempts. Don't be afraid to try things. The worst that will happen is that the script won't do what you want it to, and you'll have to try something else. You'll make mistakes, but that's part of the process. The more experience you gain, the faster and easier it will become. But if you don't try to write some scripts, you'll never learn how to do it. By now you should be getting a mental picture of why we need scripts , how JAWS uses them to improve your efficiency, and how the default and application script files work together. The next two chapters of Part 1 are titled “The Script Manager” and "Creating Simple Scripts." These are followed by Part II, "Creating Advanced Script Files." Anyone who is not thoroughly familiar with the Script Manager and all of the tools it contains for creating and documenting scripts should read the chapter describing the Script Manager. Then, if you want to learn to create scripts to read textual information from the screen and to perform other simple functions, continue with the “Creating Simple Scripts” chapter. Those who want to learn to create advanced scripts that involve dealing with windows structure, using programming constructs and manipulating JAWS settings and who already understand how to write simple scripts should skip this chapter and continue with Part II, "Creating Advanced Script Files." Homework Assignment #1 Here is your first homework assignment and a chance to get a little more experience in script writing. You probably noticed during the writing of the last script that the file menu in WordPad doesn't just show the last file you opened, it shows the last four files opened. Modify the preceding script so that it reads all four of these files, not just the first one. One solution to the problem is shown in Appendix A (See Chapter 0.), but don't look at it until you've had a good try at making it work yourself. The Script Manager The use of the Script Manager, the script creation dialog, and the various other tools for creating scripts and script files will be discussed in this section. As briefly described earlier (See Chapter 2.2.), the Script Manager is more than just a text editor. It provides the user with a suite of tools that make script and script file creation much easier. We’ll divide the description of the Script Manager into five parts. These are (1) the menus, (2) the various Script Manager file types, (3) include statements, (4) the New Script and Script Information dialogs, and (5) the Insert Function and Insert PerformScript dialogs. The Menus A detailed description of the Script Manager menu items can be found in Appendix B. Script Manager File Types We have already mentioned that there is more than one type of file which the Script Manager can generate. Listed below are the five types of files associated with the Script Manager and what each one is for. JSS - As discussed above, this is the JAWS Script Source file which the user creates to make application-specific script files. JSB - This is the JAWS Script Binary file which is created by the Script Manager when you save and compile a JSS file. This file is neither directly created nor edited by the user. JSM - This is the JAWS Script Message file and contains all of the spoken messages used by script Say and SayMessage functions. When one creates advanced script files that are to be distributed internationally, it is preferable to use message numbers in the Say and SayMessage functions rather than place the actual text there. This makes it easier to translate the messages into other languages. The JSM file contains the statements which tell the script file what to speak for a given message number. These message numbers are actually just constants that are defined as being equal to the message you want spoken. If you look at the structure of the DEFAULT.JSM file, you will see that the message numbers are defined in exactly the same way as all other constants. For example, the default JSM file, DEFAULT.JSM, contains statements like MSG1_L = “End” and MSG2_L = “home”. You will note that the statements are placed one to a line and a comma is at the end of every statement except the last one in the file. If a script in DEFAULT.JSS encounters the line Say (MSG1_L, OT_JAWS_MESSAGE), it will say “end”, and if it encounters the line Say (MSG2-L, OT_JAWS_MESSAGE), it will say “Home”. If you are creating small script files for personal use, you may not wish to bother with JSM files, but if you encounter message numbers in scripts written by others, you will know to look in the associated JSM file to find out to what the message number refers. A discussion of long (-L) and short (-S) messages and their utilization for customized verbosity settings is presented later. (See Chapter 8.9.) JSH - This is the JAWS Script Header file, and it contains other information relevant to the associated JSS file such as definitions of constants and global variables. We have already briefly discussed constants and variables and will be talking about them more extensively below. Global variables are simply variables that can be used by more than one script. It is necessary to tell JAWS what the value of a constant is, or it will not know that information. Similarly, it is necessary to tell JAWS that you will be using a certain variable with a certain name, or JAWS will have no idea that this particular string of letters is to be considered a variable. This process of telling JAWS about the constants and variables we will be using is called declaration. If you just have a few constants and global variables, you can place them directly into the top of the JSS file you are creating. This procedure will be discussed more fully below. Regular or local variables are always declared within the script itself. However, if you have large numbers of constants and global variables, it is better to place them in a JSH file. Examine the files HJGLOBAL.JSH and HJCONST.JSH for examples of files containing global variables and constants. JSD - This is the JAWS Script Documentation file and contains all of the descriptive information about each script which you enter when you create a new script by using the New Script Dialog. This information is entered into the JSD file automatically when you create a script in this fashion. This file can be edited manually if desired. Include Statements Let’s say you decide to use a JSM and/or JSH file as parts of a set of script files you are writing for a new application. So you go ahead and create these files, but then what? How does the main script file, the JSS file, know that these other files exist and are part of the script set? Take a look near the top of DEFAULT.JSS, and you will see statements like the following: Include "hjglobal.jsh" ; default HJ global variables Include "HJCONST.JSH" ; default HJ constants Include "default.jsm" ; message file These are called include statements, and their purpose is to tell the JSS file that all of the information in the included files is part of the set. When you save the JSS file and create the JSB file, all of the information in the included files will also be compiled into the resulting JSB file. Thus, the scripts in the JSS file will know about the definitions contained in the included files. The syntax for these include statements should be followed exactly as shown. Note that the file name includes the extension and is enclosed in quotation marks. Everything after the semicolon is a comment and is simply there to provide the reader with information. Everything following a semicolon on a given line is ignored by the compiler. These comments are optional. The New Script and Script Information Dialogs The New Script dialog is found under the Script menu, and the Script Information dialog is found under the View menu as the Documentation option. These two dialogs are the same, except that the New Script dialog is displayed when one first creates a script, and the Script Information dialog is displayed when one wishes to review documentation for a script which already exists. The accelerator keys for these two dialogs are CTRL+E AND CTRL+D, respectively. Most of the menu options in the Script Manager have accelerator keys, and the user can easily become familiar with the ones used frequently by examining the menus. There are two tabs in these multi-page dialogs, the General tab and the Parameters tab. Each of these is described below. General Tab The entries in the General tab are as follows: Script Name - This is where you enter the name of your script or function. It is helpful to use a name descriptive of what the script does. You may use several words concatenated together. Start each word with a capital letter so JAWS will pronounce the name as separate words. No spaces are allowed in the name. Can Be Attached to Key - This is a checkbox. If you check it you will create a script. If not, you will create a function. Remember that scripts are attached to keys and functions are not. Synopsis - This field should contain a brief statement of what the script does. This is used if you enter the Keyboard help mode (INSERT+1) or Key Word help (SHIFT+F1) in the Script Manager. The synopsis is accessed by pressing the key combination you are interested in after turning on Keyboard Help. Description - This field should contain a more detailed explanation of what the script does. This description is used if you enter the Keyboard help mode (INSERT+1) or Key Word help (SHIFT+F1) in the Script Manager. The description is accessed by quickly pressing the key combination you are interested in twice after turning on Keyboard Help. Category - You can type in a category name or choose one from the combo box. This Category field isn't currently used by any JAWS feature, but it may be implemented in the future to allow sorting of your scripts by category. Assign to Hot Key - This field is only available if you checked the “Can Be Attached to Key” checkbox. Depress the key combination you wish to use for a hot key to enter your choice. If the choice you make is already assigned to another script, you will hear a warning message and be given the opportunity to continue with the assignment or choose a different one. Function Returns - This choice is only available if you did not check the “Can Be Attached to Key” checkbox. The five choices are Handle, Int, Object, String, and Void. Select one of the first four if your function is designed to return one of these types of data to the calling script. Select Void if you don't need to use any value returned by the function. Whichever one you choose will appear on the first line of the script before the word Function. A more detailed discussion of returns will be given later. (See Chapter 7.5.3.) Return Description - This choice is only available if you did not check the “Can Be Attached to Key” checkbox. This field should contain a brief description of what information is being returned by the function and how the information is meant to be used. Parameters Tab This tab contains information about parameters used by the function, if any. A parameter is data that the function needs to have in order to do its job. As with variables, the data can be in the form of an integer, string, handle, or object. This tab is never used if you are creating a script rather than a function. It is also not used if the function needs no parameters. Entries for this tab are as follows: Existing Parameters - This listbox will show already existing parameters, if any. It will also show parameters as you add them. You can arrow up and down to select a parameter for subsequent deletion. New Parameter - If you wish to add a parameter, type its name in this field. By Reference -To understand this item, you must understand that parameters are used to transfer data from the calling script to the function. Normally, this data transfer is a one way street. This default situation is known as passing the parameter "by value." Thus, when you call the function from the script, the current value of the parameter as it exists in the script will be copied and sent to the function. During the execution of the function, it is possible for the value of the parameter to change. Since the parameter information exchange is normally a one way street when the data is passed by value, the script will not be aware that the parameter's value has changed. When the function is done and returns control to the script, the script will continue on with its original value for the parameter. Checking the By Reference checkbox will change the one way street into a two way street. If this checkbox has been checked, changes in the value of the parameter that occur during the execution of the function will be known by the calling script. This makes it possible to change a parameter's value within a function and then have the script use that changed value. This is because passing by reference passes the data's memory address rather than the value to the function. If the function changes the value at this address, the calling script or function, which uses the data at the same address, will use the changed value. Description - This field should contain a very brief description of what the parameter is for. Available Types - You should choose either Handle, Int, Object, or String from this listbox, depending on which type of information this parameter is meant to pass. As will be discussed later, handle refers to a window handle. (See Chapter 6.) Add - This button will be available if you have filled in the preceding fields. Use the SPACEBAR or ENTER key to add your new parameter to the Existing Parameters listbox. Remove - This button will be available if you have highlighted an entry in the Existing Parameters listbox. Choosing this button with the SPACEBAR or ENTER key will delete the highlighted parameter from the list. The Insert Function and Insert PerformScript Dialogs Under the Script menu, there are two selections, Insert Function Call and Insert PerformScript. These are used to insert functions and to call other scripts from within a script. The use of these two tools is described below. Insert Function Call - This brings up the Insert Function dialog, which is a list of over two hundred functions which you can use in your script construction. You will hear the title “Insert Function 1.” The term Function 1 means you are at the first level of this dialog. The first field in this dialog is an edit box. The next tab is an alphabetical list of all the available functions. If you happen to know the name of your function, you can start typing it in the edit box, and the highlight in the alphabetical list will automatically move to the function whose name starts with the same letters you’ve just typed in. You will also notice that a description of the highlighted function is read to you each time you press another keystroke. As soon as you’ve typed in enough letters to hear the name of the function you are searching for, just press ENTER to select that function. If you don’t remember the exact name of the function, just type in enough of the name to get you to the right part of the list, then TAB over to the list, and arrow up and down until you find the function you want. You can also type in function names while you are in the list, and the Script Manager will move to the function with the correct name. You will also hear function descriptions as you arrow up or down in the list. When you hit the ENTER key to select a function, one of two things will happen. If this function does not require any parameters, you will be returned to the main Script Manager editing window, and you will see that the function has been added to your script. However, if the function does require one or more parameters, another dialog containing an edit field will pop up asking you to insert the parameter. For example, if you choose SpellString as a function, you will be placed in an edit field where you can insert the text or message you wish spelled. This is the parameter required by the SpellString function. If the function you have chosen requires you to choose another function as the parameter, you can hit the key combination ALT+I, and a new dialog will open which looks very much like the Insert Function Dialog. You will hear the title “Insert Function 2”. The only difference is that the only functions that show up in this list are the ones the Script Manager thinks are appropriate choices for this parameter. Therefore, not all of the functions of the main list will appear in this sub list. You must either choose a function from this list or type the function name directly into the parameter edit field when it first appears. Then hit Enter. Continue with this process until all of the required parameters have been entered. Sometimes you will even have to go to a third or higher level, i.e., “Insert Function 3”, “Insert Function 4”, etc. by hitting ALT+I again. (This process of using functions as the parameters for other functions is called "nesting", and it will be discussed more fully later in the section titled "Using Nested Functions." See Chapter 8.6.2.) The Script Manager will then return you to the main editing window, and your function with all of its parameters will be present in the script. While you are in the Insert Function dialog, you can also TAB over to the Description and Returns fields to examine information about the function description and return information, if any, in more detail. It is also worth noting that the function list in this dialog contains both the hard coded functions built into JAWS and the functions that have been defined by any users in the current script file. Thus, if you define a new function for the script file, you will see it appear in the function list after you compile. Insert PerformScript - When you select this option, you will be placed in a list box containing all of the available scripts you can call from the present script you are writing. You can arrow through this list or start typing the name of the script to jump to it. Pressing ENTER on one of the names, for example, a script called “SayMyName,” will cause the line shown below to appear in your script. PerformScript SayMyName() This line will cause the script “SayMyName” to be executed, just as if you had hit the keystroke yourself. This is a way to reuse scripts you have already written without reproducing the code all over again. Creating Simple Scripts An example of a simple script is one in which you might wish to read some text located somewhere on the screen with a single keystroke. One way to do this would be to select a cursor, move it to the specific point on the screen where the text is, and then read the text. In this section, we'll go over how to create scripts by putting together functions that do those sorts of things. Script Documentation Each script has associated documentation. As discussed above, you enter this documentation as you create a new script. Simple scripts are always assigned to keys and require only five pieces of documentation. The following is a description of each field that is required for a simple script. Script Name - Enter the name for your script. Can be attached to Key checkbox - Always check this box for key-bound scripts. Synopsis - Provide a brief description of your script’s purpose. Description - Provide additional information about your script. Category - Choose a category for your script. You assign the category names either by typing them in or choosing them from the edit combo list. Try to choose meaningful ones so that you can list them by logical category later, in the event that this capability is added to the Script Manager. Individual Script Structure Remember that a script file is made up of one or more scripts. As discussed above, a script must have the following pieces in this exact order: First is the script begin keyword. This is simply the word "Script" followed by a space. On the same line following the word "Script" is the script name. This is usually several words concatenated together that describe the action of this script, such as, CloseDocumentWindow. Note that each of the concatenated words is initial capped, so JAWS can read the name correctly. () follows the name to complete the script begin statement. All of this is automatically entered by the Script Manager when you create a script using the New Script Dialog. (If you find that your copy of JAWS is not pronouncing concatenated words like this properly, check the Mixed Case Processing option of the Text Processing section of the Set Options menu in the Configuration Manager. This checkbox should be checked.) Next comes the body of the script. This consists of all the instructions you add during the script writing, such as variable declarations, flow control statements, function calls, PerformScript statements, arithmetic operations, and comments. (You will learn what all of these types of instructions are as we proceed through the manual.) This could be as simple as just saying a string as we did in “Our First Script” or many functions to perform a complex operation. The script end keyword , EndScript, is always the last line of a script. This is automatically entered by the Script Manager when you create a script. Using Script Functions Now, let's go over some of the commonly used functions for moving around on the screen and reading text. Reading Text Below are some functions used to read text from the screen. SayCharacter() - Reads the character at the position of the active cursor. SayWord() - Reads the word at the position of the active cursor. SayLine() - Reads the line at the active cursor. SayToCursor - Reads from the beginning of the line up to the active cursor. SayFromCursor - Reads from the active cursor to the end of the line. SayColor - Says the font color at the active cursor. SayFont - Says the font style and point size at the active cursor. SayTextBetween - Speaks all text between two column coordinates on the line of the active cursor. SayWindow - Reads the window located at the active cursor. SayToBottom - Reads all text from the position of the active cursor to the bottom of the window. SayString - Speaks string data, usually either the contents of a string variable or a specific message. If actual text is used, it should be placed inside of quotation marks within the parentheses that follow the function name. If a variable or constant is used, no quotation marks should be present. It is now recommended that the SayString function be avoided in favor of the SayMessage function (see below). Say - This function speaks a string of text, much like the SayString function, but it has a second parameter called an output mode. These output modes allow the message to be spoken with a particular set of speech characteristics. It is possible to use separate output modes to speak title lines, dialog controls, menu items, etc, with different voices or at different verbosity levels. By using the SayMessage function instead, it is possible to assign short and long messages to many output types for JAWS Help and other information. SayMessage - This function takes short and long messages supplied as parameters and speaks the appropriate message based on the specified output type, also supplied as a parameter. This function is similar to Say, except that it is possible to assign short and long messages to many output types for JAWS Help and other information. It is now recommended that the SayMessage function be used in preference to the SayString function. SayAll - Says all readable information from the point of the active cursor to the bottom of the window. If the PC cursor is active, JAWS scrolls the screen by moving the PC cursor down. If the JAWS cursor is active, the rest of the window is read by moving the JAWS cursor down a line at a time. SayCharacterPhonetic - Uses special pronunciation rules to read the character located at the position of the active cursor. Thus, A is pronounced alpha, B bravo, etc. The associations between characters and their phonetic pronunciations are made in the [PhoneticSpell] section of .JCF files. Which words are used can be changed by the user, if desired. SayControlInformation - Handles the speaking of controls for which SayWindowTypeAndText and related functions do not sufficiently describe the control. This function is designed to speak a control that requires custom processing (any control for which SayWindowTypeAndText do not properly speak the name and type of the control). This function is designed to honor the user's output mode settings for each component of the control's description. This function takes six parameters, five of which are string parameters. The first parameter is the window handle of the control that is to be spoken. The next five parameters include one parameter for each component of a control's description. Each string parameter has a corresponding Output Mode. The 5 string parameters are String strControlName = Either OT_DIALOG_NAME, OT_DOCUMENT_NAME, or OT_CONTROL_NAME, depending on the type of window that is to be spoken, String strControlType = OT_CONTROL_TYPE, String strControlState = OT_ITEM_STATE, String strContainerName = OT_CONTROL_GROUP_NAME, and String strContainerType = OT_CONTROL_TYPE. This function works by building a string based upon the five components of the control description, adding each component only if the user has specified that this item should speak in the current verbosity level. Then this function calls the Say function with the constructed string as the first parameter and OT_NO_DISABLE as the second parameter. TsayFocusRect - Says the contents of a focus rectangle. Returns TRUE if any text was spoken, FALSE otherwise. A TrackFocusRect=1 statement should be added to the [OSM] section of the application's JCF file for this function to work properly. SayFocusRects - Says the contents of focus rectangles. If there is only one such rectangle, this is exactly like SayFocusRect. If there is more than one, this function says the contents of all of them, while SayFocusRect says only the first one. A TrackFocusRect=1 statement should be added to the [OSM] section of the application's JCF file for this function to work properly. SayChunk - Speaks the chunk of information to which the active cursor is pointing. A "chunk" is text and graphic information that was written to the screen in a single operation. SayChunk is similar to SayField, however, the SayField function uses logic to determine the text that is to be spoken, while SayChunk simply reads the text that was stored in the Off Screen Model as a single unit. SayField - Reads the field of text where the active cursor is pointing. A "field of text" is a section or block of text that has a common attribute, i.e., bold, underlined, italics, or strikeout. The use of the attribute must be contiguous. The SayField function uses logic to determine the text that is to be spoken, while the SayChunk function simply reads the text that was stored in the JAWS Off Screen Model as a single unit. SayFrame - Speaks the contents of the specified frame. SayFrameAtCursor - All text within the boundaries of the frame that contains the active cursor is spoken. SayInteger - Speaks numeric, integer data, often the contents of an integer variable. SayParagraph - Reads the current paragraph from the beginning SaySentence - Reads the sentence containing the character on which the active cursor is positioned. SayUsingVoice - Speaks a string of text using a specific set of speech characteristics called voice context. SayCell - When in a table or spreadsheet, speaks the contents of the current cell. SayColumnHeader - When in a table or spreadsheet, speaks the contents of the column header. SayRowHeader - When in a table or spreadsheet, speaks the contents of the row header. Moving Around on the Screen Before you attempt to read something from the screen with a script, you'll probably have to move one of the cursors to the location of the item you wish to read. Therefore, it's important to understand something about cursors. For those using JAWS without a Braille display, there are four kinds of cursors, the PC cursor, the JAWS cursor, the Invisible cursor, and the Virtual PC cursor. Those using a Braille display will have a fifth cursor, the Braille cursor. (This cursor is only used internally by the Braille scripts and should never be left on after a script completes its work.) If you are editing or entering text, the PC cursor is where characters would be placed when you type. (This editing or text insertion form of the PC cursor is also called a caret.) However, when you are using a menu or tabbing through some selections such as buttons in a dialog box, there is no caret on the screen. In these situations, the PC cursor is the focus. If you wish to read other areas of a window or read a part of the screen where the PC cursor cannot go, you can switch to the JAWS cursor. The JAWS cursor can be moved anywhere within the boundaries of the current real window, making it possible to explore areas of the screen where the PC cursor cannot go. A real window is defined as a window that has a title. This means that your JAWS cursor is normally only restricted to movement within the boundaries of the highest level parent window with a title, starting from the window you are in currently. This is what we mean when we say the JAWS cursor is restricted by the boundaries of the current real window. (If you wish to restrict the JAWS cursor to the window you are currently in, even if it is not a real window, you can do so by using the keystroke combination, INSERT+R.) Since the JAWS cursor always moves the mouse pointer to its location, the mouse pointer is always positioned and ready to be clicked at the location of the JAWS cursor should you decide to do so. The third kind of cursor is called the Invisible cursor. In one way it is just like the JAWS cursor as its movement is also only restricted by the boundaries of the real window, but it has no visible entity on the screen since it does not bring the mouse pointer with it. The Virtual PC cursor is a special case of the PC cursor which is turned on automatically in certain applications such as Internet Explorer 5, Outlook/Outlook Express, and Eudora 4.X where a Microsoft browser or browser style viewer is being used. The Virtual PC cursor is created within a special JAWS buffer and is employed to allow the user to have a cursor that can navigate around the viewer window even though that viewer does not actually have a normal PC or caret. The user is actually navigating in the virtual buffer created by JAWS and feels as if navigation were occurring within a word processor type of application. This makes reading in such applications much easier. How do we use these cursors? It’s generally impractical and often impossible to go exploring around the screen with the PC cursor. Therefore, before we go to read some text from the window, we usually first switch to one of the other two cursors. One can pretty much use either the JAWS or the Invisible cursor to do the reading for you, so to some extent it’s just a matter of choice. However, there are some situations where you will definitely want to use one of these two cursors in preference to the other. If you plan to click a button or take some other action with the mouse, it will be necessary to use the JAWS cursor. Since the mouse pointer accompanies the JAWS cursor in its travels, it is then a simple matter to click the mouse as it’s already in the correct position. If you do not wish to move the JAWS cursor from its current position, or there won’t be any need to click on anything, the Invisible cursor is the best choice. There is one particular situation which comes to mind when you definitely won't want to move the JAWS cursor. Sometimes in Windows, information text will appear somewhere on the screen, usually the status line, when the mouse pointer is at a particular location. If you move the JAWS cursor to read the text, the mouse pointer will move with it, and the text will disappear. In this situation, you must use the Invisible cursor to read such text. In general, it's a good idea to use the Invisible cursor to read text when no clicking is necessary, unless there is a particular reason to use the JAWS cursor. In some cases, it is necessary to move one of the cursors such as the JAWS cursor, even though you want it to be back in its original location when the script is finished. For this reason, there are the SaveCursor and RestoreCursor functions that you can use to put everything back as it was before your script was executed. Here is a list of the functions that you can use to select and manipulate the cursors. PCCursor() - Activates the PC Cursor. JAWSCursor() - Activates the JAWS Cursor. InvisibleCursor() - Activates the Invisible Cursor. BrailleCursor () - Activates the Braille cursor. RouteBrailleToPc () - Moves the Braille cursor to the PC cursor. RouteBrailleToJAWS () - Moves the Braille cursor to the JAWS cursor. RouteJAWSToBraille () - Moves the JAWS cursor to the Braille cursor. RoutePCToBraille () - Moves the PC cursor to the Braille cursor, if possible. RoutePCToJAWS() - Moves the PC Cursor to the JAWS Cursor, if possible. RouteJAWSToPC() - Moves the JAWS Cursor to the PC Cursor. RouteJAWSToInvisible() - Moves the JAWS Cursor to the Invisible Cursor. RouteInvisibleToPC() - Moves the Invisible Cursor to the PC Cursor. RouteInvisibleToJAWS() - Moves the Invisible Cursor to the JAWS Cursor. SaveCursor() - Saves the active cursor and its position. RestoreCursor() - Reactivates the saved cursor and restores it to its original position. The next group of functions is used to move the cursor. PriorCharacter() - Moves the active cursor to the prior character. PriorWord() - Moves the active cursor to the prior word. PriorLine() - Moves the active cursor to the prior line. NextCharacter() - Moves the active cursor to the next character. NextWord() - Moves the active cursor to the next word. NextLine() - Moves the active cursor to the next line. NextParagraph - Moves the active cursor to the beginning of the next paragraph. If the PC cursor is active, and the next paragraph is not already visible, then text in the window will automatically scroll to bring it into view. NextSentence - Moves the active cursor to the beginning of the next sentence. If the PC cursor is active, and the next sentence is not already visible, then text in the window will automatically scroll to bring it into view. PriorParagraph - Moves the active cursor to the beginning of the prior paragraph. If the PC cursor is active, and the prior paragraph is not already visible, then text in the window will automatically scroll to bring it into view. PriorSentence - Moves the active cursor to the beginning of the prior sentence. If the PC cursor is active, and the prior sentence is not already visible, then text in the window will automatically scroll to bring it into view. JAWSHome() - Moves the active cursor to the beginning of the line. JAWSEnd() - Moves the active cursor to the end of the line. JAWSPageUp() - Moves the active cursor to the top of the window. JAWSPageDown() - Moves the active cursor to the bottom of the window. MoveTo - Moves the JAWS or Invisible cursor to the screen coordinates specified by the user. MoveToControl - moves the active cursor to a specific control within a window. If the PC cursor is on when this function is called, the JAWS cursor is turned on automatically. Otherwise the active cursor is used. MoveToFrame - Moves the active cursor to the top left corner of the specified Frame. If the PC cursor is active when this function is used, then the JAWS cursor is activated and it is moved to the new position, otherwise the active cursor is moved. MoveToGraphic - Moves the JAWS cursor, Invisible cursor, or Braille cursor in a specific direction to find a graphic symbol in the active window. MoveToWindow - Moves the active cursor to the specified window. If the window contains text, then the cursor is positioned on the first character. Otherwise, it is positioned at the window's center. If the PC cursor is active when this function is used, then the JAWS cursor is activated and it is moved to the new position. PriorChunk - Moves the active cursor to the prior chunk of text. A chunk of text is a section or block of text that is written to the screen at one time NextChunk - Moves the active cursor to the next chunk of text. A chunk of text is a section or block of text that is written to the screen at one time DownCell - When inside a table or spreadsheet, moves the active cursor to the cell in the same column but the next Row. UpCell - When inside a table or spreadsheet, moves the active cursor to the cell in the same column but the previous Row. PriorCell - When inside a table or spreadsheet, moves the active cursor to the cell in the same row but the previous column. NextCell - When inside a table or spreadsheet, moves the active cursor to the cell in the same row but the next column. Note that If your purpose is to read something located on the screen, you would usually follow a move function with one of the say functions. An example of this is shown below. JAWSPageDown () ;moves to the bottom of the window SayLine () ;reads the line moved to Creating Reading Scripts Now, let's look at a practical use of what we have learned so far. Before we look at a real script, here are the steps again: First, save the status of your current cursor, if necessary. Then activate the cursor of your choice. Now move the active cursor to a specific position in the window. Select the appropriate reading function. Restore your original cursor if you used the SaveCursor function at the start of the script. The following are two variations of a script which can be used to read the bottom line of a window. There is a comment at the end of each line that describes the function. (We previously mentioned comments briefly in the section on include statements, and will discuss them more fully below.) In the first script we will switch to the JAWS cursor, read the bottom line and then leave the JAWS cursor active at the bottom of the window. Script ReadBottomLine() ; Begins the script and assigns it the name, ReadBottomLine JAWSCursor(); Activate the JAWS cursor RouteJAWSToPC() ; Move the JAWS cursor to the PC cursor to assure it is in the window we're working ;in and not in some other window elsewhere on the screen. JAWSPageDown() ; Move the JAWS cursor to the bottom of the window JAWSHome() ; Move the JAWS cursor to the beginning of the bottom line SayLine() ; Speak the bottom line of the window EndScript ; End of the script The next variation adds the save and restore cursor functions and uses the Invisible cursor. This script will read the status line and then restore the original cursor. Script ReadBottomLine() ; Begins the script and assigns it the name, ReadBottomLine SaveCursor() ; Saves the current cursor and its position InvisibleCursor() ; Activates the Invisible cursor RouteInvisibleToPC() ; Move the Invisible cursor to the PC cursor so it is in the correct window. JAWSPageDown() ; Move the Invisible cursor to the bottom of the window JAWSHome() ; Move the Invisible cursor to the beginning of the bottom line SayLine() ; Speak the bottom line of the window RestoreCursor() ; Reset the cursor and position we saved EndScript ; End of the script See how it works? You could use this script to read the status line in Microsoft Word. The status line tells you the page number, line number, column number and other information like that. If you only wanted the page number, you would substitute a SayWord() function followed by a NextWord () and another SayWord () for the SayLine() function. Then you would just hear the words: page and the number. There is an important subtlety about the SaveCursor and RestoreCursor functions which needs to be understood. These functions save and then restore the cursor type of whatever cursor is active when the SaveCursor function is used. In addition, if the JAWS or Invisible cursor is the active cursor when the SaveCursor function is used, the cursor position is also saved and then restored by the RestoreCursor function. Cursors which are activated and moved after the SaveCursor function is used will not be restored to their original types or positions. Thus, in the above example, the position of the Invisible cursor would not be restored unless it happened to be the active cursor at the time the SaveCursor function is called. However, it is possible to use multiple SaveCursor and RestoreCursor statements in one script. These statements will "stack" in very much the same way that the default and application scripts stack. This means that, if you were to use a second SaveCursor statement, and you were to have a different cursor active than you did when you used the first SaveCursor statement, it is the position of the second active cursor which would be saved when the second SaveCursor is executed. Furthermore, the RestoreCursor statements will undo the SaveCursor statements in the reverse order. Thus, if you perform two SaveCursor statements and then perform a RestoreCursor, it is the second SaveCursor that will be reversed. To undo the first SaveCursor, you would have to perform a second RestoreCursor. The following example illustrates the stacking of SaveCursor function calls: Script ReadBottomLine () ; Begins the script and assigns it the name, ReadBottomLine SaveCursor () ; We don’t know what cursor was active, but Saves the current cursor and its position InvisibleCursor () ; Activates the invisible cursor SaveCursor () ; Saves the invisible cursor and its position RouteInvisibleToPC () ; Move the Invisible cursor to the PC cursor so it is in the correct window. JAWSPageDown () ; Move the invisible cursor to the bottom of the window JAWSHome () ; Move the invisible cursor to the beginning of the bottom line SayLine () ; Speak the bottom line of the window RestoreCursor () ; Reset the invisible cursor and position we saved RestoreCursor () ; Reset the users original cursor and position we saved EndScript ; End of the script Finally, if you use a SaveCursor ()in a script, JAWS automatically does a RestoreCursor () at the end of the script, even if you forget to put one in. JAWS assumes that you want to restore the cursor if you've put a SaveCursor () in the script. If you've used multiple SaveCursor statements, JAWS will automatically undo all of them in reverse order when the script is finished, even if you don't remember to put in all of the RestoreCursor statements. Putting It All Together Now let's look at the process of creating a script, assigning it to a key, and compiling the script file so we can use it. Here's the plan. We will create a script for the Script Manager that reads the current line number. We'll need the Script Manager for this task. Remember that we used this editor in an earlier section (See Chapter 3.)when we created our first scripts. This time, we'll let the Script Manager do more of the work for us. Follow these steps exactly: Start the Script Manager by selecting it from the JAWS Utilities menu. Press INSERT+Q to verify the Script Manager settings are loaded into the stack above the default script file and that the program name for the script editor is JSCRIPT.EXE. This means, of course, that the script file currently controlling JAWS is JSCRIPT.JSS. To load this file so you can edit it, drop down the file menu with ALT+F and arrow down to Open and press ENTER. Type in the file name, JSCRIPT.JSS, and press ENTER. Finally, press CTRL+END to take the caret to the bottom of the script file, the best place to add a new script. Before we actually write our script, let's explore the window to see what we want our script to read. Script Manager has a status line at the bottom that contains a help message, the current line number and the total number of lines in the file. We want to read only the current line number with our script. Just to make sure we know what we want our script to do, let's do it manually first. Start by activating the JAWS cursor. Then press PAGE DOWN to move to the bottom line followed by HOME to move to the beginning of the bottom line. Now use INSERT+NUM PAD 5 to say the current word and you should hear the word "For." Use the next word key (INSERT+NUM PAD 6) to read across the line. You now know that the fifth word on this line is the current line number. We now know what our script has to do. It must activate a cursor, position it at the proper place, and then read the word. OK, reactivate the PC cursor and we'll get started. Select New Script from the Script menu, and the New Script dialog will appear. Now we have to name our script and write its documentation. When the New Script dialog appears, the cursor is in the Script Name field. Type in SayLineNumber. Be sure to cap the S, L, and N and don’t put in any spaces. Press TAB to move to the Can be attached to key checkbox and press SPACEBAR to check it. We want to attach our script to a keystroke later. Press TAB to move to the Synopsis field and type, “Read current line number.” Press TAB to move to the Description field and type, “The line that contains the PC cursor.” Press TAB to move to the Category field and type, "Say," because this script will say something. Press TAB to move to the Assign To field and press CTRL+SHIFT+L to assign this keystroke to SayLineNumber. We won’t need any other fields filled in for our script, so press ENTER to close the New Script dialog and insert our blank script into the editing area. We now have a script begin statement with the script name and an EndScript statement. There are a few blank lines in between for us to put in our script statements and the cursor is positioned on one of the blank lines. Use the UP ARROW key until you reach the first blank line of the script. This time we will not type our functions as we did when creating our first script. We will use the Insert Function dialog of the Script Manager. Choose Insert Function Call from the Script menu, or use the hot key combination, CTRL+I. The Insert Function dialog box appears and the cursor is placed in the function name edit field. There is also a list of functions displayed beneath the function name field. You could tab down to the list and start arrowing through the functions to find the one you need, but that would not be very efficient as there are more than two hundred of them. JAWS has a better way, but it does require that you have some idea of the function name. Just like in our example earlier, we need to start with saving the cursor and position. The function is SaveCursor(), but we only need to press the S as the Insert Function tool starts searching as soon as you start typing. SaveCursor() is the first function that starts with an S. Notice that as focus moved to SaveCursor(), a help message was read that explained how this function is used. Press enter and this function is placed into our script. Easy isn't it? Now let's activate the Invisible cursor. We use the Invisible cursor here as we just want to read the line number and return. No sense in dragging the mouse with us, so we won't use the JAWS cursor. Press ENTER to insert a new line and then press CTRL + I again. The function we need this time is InvisibleCursor(). We'll have to type two characters this time as InvisibleCursor() is not the first function that begins with an I. Type slowly so that you can hear each function as it is highlighted. Once you hear InvisibleCursor(), press ENTER to place it in our script. Use the same procedure to add the rest of our functions in this order: RouteInvisibleToPC (), JAWSPageDown(), JAWSHome(), NextWord() five times, SayWord() and RestoreCursor(). Press CTRL + S to compile the script file. If you made no mistakes, you should hear the "compile complete" message. If not, repeat the previous steps until you can compile without an error. If you do get an error, the Script Manager will position the caret near where it thinks the error is so you will have a better chance of finding it. Now test your work by pressing CTRL+SHIFT+L to hear the current line number. Try a few variations like reading the whole line instead of the word. Then add a message that says "line number" before it actually speaks the number. When you feel comfortable with this process, either continue with the next part, "Creating Advanced Script Files, or jump in and write some scripts you been wanting for some time. Homework Assignment #2 Here is your second homework assignment. As you probably know, most windows have three states, minimized, restored, and maximized. In the upper right hand corner of most windows, there are three buttons. The first is minimize, and the last is close. The middle one can either be restore or maximize, depending on the current window state. By moving to and reading the middle button, you can determine the window state. If it says "restore symbol", then the window is maximized. If it says "maximize symbol", then the window is restored. Your assignment is to add a script to the default script file, DEFAULT.JSS, which will read this button. Obviously, to test your script properly, make sure you are in a window that has these buttons. The Script Manager window will serve quite nicely. After you have written this script, you will be able to determine the state of the current window with one keystroke. For this assignment, we want you to use the JAWS cursor to do the reading, and you should make sure that the JAWS cursor is returned to its original position by the time the script ends. Assume that the PC cursor is active when you start the script, and make sure it is active when the script is done. You can load the default script file by choosing the Script Manager from the JAWS window Utility menu and then loading DEFAULT.JSS. An alternative way of doing this is to press CTRL+SHIFT+0 on the number row. This will automatically open the Script Manager and load the DEFAULT.JSS file. Add your script to the bottom of this file. One possible answer can be seen in Appendix A (See Chapter 14.1.), but don't look at it until you've had a try at writing this script. Extra credit - If you're really feeling adventuresome, try modifying the above script to be a little more sophisticated. Instead of just reading the Maximize or Restore symbols, use If-Then, ElIf, Else, GetWord, and Say functions to decide what the state of the window is and what message to speak. If the window is showing a Restore symbol, have the script say "Maximized." If a Maximize symbol is showing, have the script say "Restored." If it doesn't find either of these two symbols, have it say "Couldn't find the symbol." Hint - a labeled graphic such as a Restore or Maximize symbol can be treated as text, and its label can be read and manipulated with the same functions that are used for pure text. One possible answer to this problem is also in Appendix A. (See Chapter 14.1.) Homework Assignment #3 And just for fun, here is your third homework assignment. Windows comes with a sound recording program called Sound Recorder. This program can usually be found in the Multimedia folder of the Accessories folder of the Start Menu. Open it, and explore around the Sound Recorder window with your JAWS cursor if you are not familiar with this program. You will find that there are several buttons for play, stop, record, etc. You will also find that the fourth line from the top has a counter which tells you how many seconds have played and how many seconds long the file is. Load any file on your computer with a .WAV extension using the Open Dialog of the File menu. (You can find many .WAV files in the C:\WINDOWS\MEDIA folder.) Click the play button to start playing the file, then click the stop button before the file finishes playing. (If you prefer to use the keyboard to do this, the hot keys for these two functions are CTRL+P and CTRL+S, respectively.) If you look at the elapsed time counter again, you will see that there are now numbers indicating how much time is elapsed out of the total time. We want you to write a script for this application's JSS file (SNDREC32.JSS) which will read this counter for you with one keystroke. The announcement should be in the form "Current time is X seconds out of Y seconds". The X and Y values are to be obtained from the Sound Recorder display, and the rest of the announcement should be created from Say statements. Use the Invisible cursor to do the reading, and return the starting cursor to the original state when done. One possible solution to this problem is shown in Appendix A. (See Chapter 14.2.) Part II: Creating Advanced Script Files Windows Program Structure You've probably heard people use terms like window class and window handle in discussions about how Windows programs are structured. What do terms like this mean? In this section, we'll explain window hierarchy, identification, and structure so you can make use of the script functions that query and compare these items. The first question is what is a window? Well, that would seem to be simple. Programs run inside of windows. Even JAWS has a window. That’s true. The JAWS window is a window, but it might surprise you to know that every entry field, button, and other control is also a window. At least it should be. Like all other rules in computer programs, it is not always followed. A programmer may choose to create controls, such as entry fields, without making them actual windows. Programs like this are the most difficult to make accessible because there's no simple way to identify and speak the various controls. These programs need script writing even more because the scripts in the default files will not speak these windows properly. For the moment, we'll assume that the programmer assigns each control to a window. Sounds complicated doesn’t it? Let's see if we can sort it out. Hierarchy Most people who operate in the Windows environment never really understand what the word "window" truly means with respect to this environment. Of course they realize that the big rectangular thing that pops up on the screen when they open a new application is a window, and they probably also think of the text area of their word processor as a window, but there is much more to it than that. In most applications, every dialog box, menu, button, edit field, listbox, etc., is actually a separate window with one or more identification numbers that can be used to refer to it. These scores of windows that may be present on your screen at any one time are not randomly dropped there, they are interrelated with a special hierarchy that allows the Windows operating system and the programmers who work within it to keep track of them all. It will all make more sense if we look at the hierarchy of windows in the system. The metaphor most often used is one of parents and children. Here’s how it works. Your computer desktop is the parent of all application windows. That makes the JAWS Window, your word processor’s window and the Internet browser’s window all children of the desktop. These windows are all one level down from the desktop, so they are all children of the desktop at the same logical level. This makes them all peers of each other, and, when you later encounter terms such as PriorWindow and NextWindow, the reference is to moving among peer windows at the same level. OK, now let’s go to the next level. The word processor has several child windows: the text area, the menu bar, the toolbar and the status line. These are all children of the word processor window and are, themselves, all children at the same logical level. Are you beginning to get it? When you ask to open a file in the word processor, a child window appears so that you can look for the file. It’s often referred to as a dialog box, but in the grand scheme of things, it is a child window to the word processor, its parent. Now think of the Open File dialog box as a parent and then you find that the File Name field is a child window of the dialog. So are all the other fields within the dialog. These are all children of the parent dialog and are all at the same logical level. Each window is a child to the window one level up that spawned or generated it, and each window is a parent to the windows one level down that it spawns. Just in case you're having a little trouble visualizing all of this, let's try using an analogy. Most people are familiar with directory tree structure, either from working in DOS or Windows Explorer. On your disk drive, say your C drive, C:\ is the parent of all other directories on your drive. All of the subdirectories one level down from C:\ are children of that parent and are at the same logical level. Thus, if you have subdirectories named Eudora, JAWS37, MyDocuments, Recycled, and Windows on your system, they will all be one level down from C:\. They are all direct children of C:\ and are all one logical level down. For the purposes of directory tree structure and window hierarchy, you can assume that being at the same logical level means that all members were spawned or generated by the same parent. Remember that we said that the PriorWindow and NextWindow command could be used to move to windows at the same logical level? Well, in a directory tree, this would be analogous to moving to the prior subdirectory and next subdirectory of a directory tree. If we open the JAWS37 subdirectory, we will see four new subdirectories, Help, Manuals, Settings, and Tecnotes. These four subdirectories are all one level down from JAWS37, are children of JAWS37, and are at the same logical level as each other, two levels down from C:\. This can, of course, continue until we reach the lowest logical level of a particular branch. The parent/child structure of Windows works much the same way as a directory structure. Each application is a branch of the tree, and child windows are spawned from it. All windows one level down from the parent application are children at the same logical level, one level down from the parent. Each of these children can spawn children of their own, and these children are three levels down from the parent application. Try moving up and down through the various logical levels of different branches of your directory tree in Windows Explorer, and you should get a better feeling for this parent/child hierarchy. Identifying Windows The application programmer assigns a number of identifiers to each window. These identifiers allow the window in question to be referred to unambiguously. For instance, each has a Window Class, a Type, a Type Code, a Subtype Code, and a Control ID. The Window Class and Type Code tell us whether it is an edit field, a button, or some other type of window. The Subtype Code can give more specific information about the window, differentiating, for example, between a regular button and a radiobutton. Thus, the window class, Type, Type Code, and Subtype Code allow us to categorize the window. Listed below are more precise definitions of these four categories and the JAWS built-in functions that are used during script writing to obtain them. Window Classes, Types, Type Codes, and Subtype Codes Window Class - This is a category which denotes some information about what a window is and does. For example, a Window Class can be an edit field, a listbox, or a button. This is already providing some valuable information about the window since we know that a listbox is something entirely different and will have quite distinct reading requirements from a button. The problem is that Window Class does not always provide information which is sufficiently detailed for our purposes. For example, checking the Window Class will return the same information for a button, radiobutton, and checkbox, all of which have the Window Class of button. Obviously, we are going to need more detailed information in some instances. The other window categories listed below can be used to provide this information. Furthermore, some programmers use custom window classes which have strange and unrecognizable names that are not understood by JAWS. We are going to need some method of telling JAWS how to deal with these custom window classes, and this is discussed below. The Window Class is obtained with the function, GetWindowClass. Window Type - As with Window Class, Window Type returns a string or name which is descriptive of the window. This category can sometimes give more specific information about certain windows. However, asking for the Window Type will Return the same information about a window as asking for the Window Class if no more specific information is available. For example, the Window Class and Window Type of an edit field will be exactly the same. However, if more specific information is available, the Window Type can be used to sort this out. Thus, whereas the Window Class of a button, radiobutton, and checkbox is always "button", the Window Types of these three windows will be reported as "button", "radiobutton", and "checkbox". This more specific information will allow us to tell JAWS what to do for each of these types. If the programmer has used custom window classes, then asking for the Window Type will return a string that says that the Window Type is unknown. However, when a custom window type is reclassified to a known window class (see below), the Window Type will return the name of the reclassified window and, thus, tell us what the window does. One problem with the Window Type is that the string returned by this function is always in English and, thus, is not very useful for those using JAWS in other languages. For this reason, the Window Type Code was developed. The Window Type is obtained with the function, GetWindowType. Window Type Code - Asking for the Window Type Code returns a number instead of a string. This number is translated into a recognizable string by constant definitions which can be found in the file HJCONST.JSH. For example, in this file you will see lines such as WT_Button =1, WT_Edit = 3, WT_ListBox = 4, and WT_ScrollBar = 5. The WT prefix of the constant tells you that this is a constant that refers to a Window Type. While the Window Type Code does not end up providing any more information than the Window Type, it does have the advantage of being language independent. Since the same numbers are always returned for a given type of window, the numbers can be equated to a string in any language desired. The Window Type Code is obtained with the function, GetWindowTypeCode. Window Subtype Code - As with the Window Type Code, the Window Subtype Code also returns an integer. The Window Subtype Codes are translated into strings using the same constant definitions as for Window Type Code. The only difference is that the Window Subtype Code will provide more detailed information, if available. If not, both functions will return the same information. For example, a RadioButton will return a Type Code of 1 (WT_Button) but a Subtype Code of 19 (WT_RadioButton). The Start button located on the task bar will also return a Type Code of 1 (WT_Button) but will return a Subtype Code of 32 (WT_StartButton). Similarly, GetWindowTypeCode will return WT_TABCONTROL for the Task Bar whereas GetWindowSubtypeCode will return WT_TASKBAR. You will want to use GetWindowTypeCode when you don't wish to have the more specific information which is returned by GetWindowSubtypeCode. Window Subtype Codes are also language independent. They can be obtained with the function, GetWindowSubtypeCode. The reason that these window categories are important to users of JAWS is that JAWS will behave differently, depending on what category of window has the focus. For example, if the focus is on an edit field, JAWS tracks the caret or insertion point. If you hit the DOWN ARROW key, JAWS will speak the line of text to which your caret moves. On the other hand, if you are in a list box or menu, JAWS tracks the highlight or lightbar as the arrow keys are used to move it up and down the list. JAWS knows to do these things because of the scripts that are associated with the up and down arrow keys in the DEFAULT.JSS script file. These scripts contain logic that decides what to do based upon the category of the window that has the focus when you use one of these keys. If you examine the SayPriorLine and SayNextLine scripts in the DEFAULT.JSS file, you will see that decisions about what to speak are made based upon the Type Codes and subtype Codes of the windows that are current when the script is invoked. As with Window Types, Type Codes, and Subtype Codes, there is a set of standard window classes that is used by most programmers, and the JAWS default scripts contain the necessary logic to allow proper speaking of these standard classes most of the time. However, Window Classes are not always as standard as these other categories. If you have ever needed to use the Window Classes dialog in the Configuration Manager to reassign a custom or unknown Window Class, you know that sometimes a programmer will create custom window classes that we need to inform JAWS about. Since these custom classes are, by definition, non-standard, JAWS has no idea what to do with them. Therefore, we have to use the Configuration Manager to equate the unknown class to one of the standard classes so JAWS will know how to behave when it encounters it. This process is called reclassifying or reassigning the Window Class. If you have never used this procedure, use either the key combination INSERT+7 or the key combination INSERT+F2 followed by W and ENTER to invoke the Window Class reassignment dialog, and explore the various controls to become familiar with it. Once you have reclassified a window to equate it with a standard class, it should speak properly. Sometimes you may have to try more than one class to figure out which one works best. Control ID's and Window Handles The Control ID is an arbitrary number which serves as an identifier that the programmer uses to mark each window in his program. It is obtained with the function, GetControlID. The Control ID, unlike the Window Class, has no special meaning, but it can be used to refer to a particular window. Ideally, no two windows in a program should have the same Control ID, but they sometimes do anyway. If you know how to access the Control ID, and, if it is unique within the application you are writing scripts for, then you can tell JAWS to do certain things if it finds that you are focused on the window with the particular Control ID you have identified. One example where this is extremely useful is in applications where the buttons are labeled with bit map drawings instead of text labels. Normally, JAWS will speak the text label of a button when you TAB to it, but if the label is a drawing, there is no name to be spoken. By determining the Control ID numbers of the various bit map buttons, one can write a script that will speak the name of a button when the focus moves to a button with a particular Control ID. Of course, there is no way to insure that two windows in different programs won't have the same Control ID. Therefore, to prevent confusion, the system assigns a unique identifier, called a window handle, to every window when it is accessed during a given session. Window handles are numbers, but they are not unchanging values like a Control ID. As long as the handle is actively assigned to a particular window, it can be used to refer to and identify that window. Once the handle is assigned, it remains active as long as the window exists. However, when a window is destroyed, it relinquishes its handle. If the window is reinvoked later, it will probably have a different handle. Since the system assigns these handles as windows are used, the handle assigned to a window on this day may not be the same as it is tomorrow. A good analogy to the window handle would be the numbered ticket dispenser used in many bakeries and delicatessens. When you walk in you take a number from the dispenser, and this number becomes your handle. Today your number might be 31, and when the server at the counter calls 31, you know it's your turn. If you go in again the next day, your number will probably be different. It's still your handle, but it's different from what it was the prior day. The point is, it's an identification system that tells the server who's next in line. Use the window' handle to identify it among all of the other windows in the system at any given time. The window handle can be obtained using the built-in functions, GetCurrentWindow () and GetFocus (). One way to do this, for example, would be to use the GetCurrentWindow () function as a parameter for the SayInteger () function. Thus, SayInteger (GetCurrentWindow ()) would speak the window handle of the window containing the active cursor. By the way, this is another example of the use of nested functions which was mentioned earlier. There are some built-in default scripts that you can use to query these and other types of information from an application. We'll teach you how to use them in "Exploring the Application With the Utility Functions". (See Chapter 8.1.) For now, try the following. Press CTRL+INSERT+F1. You should hear the Control ID, Window Class, and Window Handle announced for the window you are currently in. Move around to different windows and different applications to get some idea of the kind of information this keystroke combination can supply. Building Blocks of the Script Language We construct script files from building blocks that make up the JAWS script language. Think of this part of the book as learning a language because you will be doing exactly that. Just as our written language has parts of speech that make sentences that make paragraphs, so also does the JAWS script language have statements that make up scripts that make up script files. First, we'll learn how an individual script is assembled, and then we'll learn all the different kinds of statements in this language. In the next part, "Script Writing Techniques," we'll learn how to put complete script files together and how to compile and use them. The Script As we've said before, a script is a small computer program that controls JAWS activities. Scripts can be attached to a keystroke. They do not have to be, but any script can be. A script must have the following pieces in this exact order: First is the script begin keyword. This is simply the word "Script" followed by a space. On the same line following the word "Script" is the script name. This is usually several words concatenated together that describe the action of this script, such as, CloseDocumentWindow. Note that each of the concatenated words is initial capped, so JAWS can read the name correctly. () follows the name to complete the script begin statement. All of this is automatically entered by Script Manager when you create a script. Next are all of the local variable declarations followed by all of the functions, arithmetic operations, and flow control statements that you need in sequential order. This could be as simple as just saying a string as we did in Our First Script or a complex operation. The script end statement, EndScript, is always the last line of a script. This is automatically entered by the Script Manager when you create a script. The Script Manager will create the basic script structure correctly when you choose New Script from the Script menu or use the accelerator key combination, CTRL+E. You should always create a script in this fashion as the Script Manager will also prompt you for the proper script documentation and let you assign a keystroke. The Function The biggest difference between scripts and functions is that a function cannot be attached to a keystroke. It will either be called from a script or another function or be triggered by a system event. It is important to remind you about some jargon we discussed earlier. This word function has several slightly different meanings, and it's important to keep them in mind for the following discussion. We are about to talk about functions that can be created by you, and we mean by this a script you write that is not attached to a keystroke. This is a user-defined function, meaning it is a function written by you, the user. There are also functions already present in script files you may be modifying, and these are functions that were written by Henter-Joyce's script writers. These are also user-defined functions, but they were defined by other users, not you. Finally, there are the built-in functions that are the building blocks of the JAWS language. You, the user, cannot modify the built-in functions as you can your own functions or the ones written by other people. You can use any of these three types of functions as building blocks when writing scripts or new functions, but the built-in functions are hard coded into JAWS, and so the following discussion does not apply to them. (Built-in functions will be discussed later. See Chapter 7.3.5.) A non-built-in function must have the following pieces in this exact order: The return type, either handle, Int, Object, String, or Void. The function begin keyword. This is simply the word "Function" followed by a space. On the same line following the word "Function", the function name. This is usually several words concatenated together that describe the action of this function, such as, SayFocusedWindow. Note that each of the concatenated words is initial capped. That way JAWS can speak the name as if it were separate words. The name is followed by () to complete the function begin statement. Note that all functions should have return type designations such as those listed under item 1 before the function begin keyword. These return types were mentioned earlier (See Chapter 4.4.1.) and describe what type of information is returned to the calling script when the function finishes running. Of course, a function can be designed simply to be called and complete its task without sending any specific information back to the calling script or function. In this case, the return is designated as Void. On the other hand, in addition to completing any other tasks, the function can also send an integer, string, object, or handle value back to the calling script. If it does this, you should see the word Int, String, Object, or Handle before the word Function on the first line. Special function event names such as AutoStartEvent or NewTextEvent tell you that a function runs automatically when certain things happen in an application. These functions are not normally called by other scripts or functions. There can also be parameters in between the ( and ) which are used to pass information to the function which it will need to perform its purpose. We'll discuss all of these in more detail later. (See Chapter 7.5.) Then you have all of the local variable declarations followed by all of the functions, arithmetic operations, and flow control statements that are needed in sequential order. This could be as simple as just saying a string with one function as we did in Our First Script or a complicated operation. The function end statement, EndFunction, always the last line of a function. The Script Manager will create the basic function structure correctly when you choose New Script from the Script menu. You should always create a function in this fashion as Script Manager will also prompt you for the proper function documentation and let you assign parameters and returns. Let's also remember one other piece of jargon. The term "calling a function" means placing that function's name into a script or function to be executed. You can call either a user-defined function or a built-in function in this manner. In many cases, rules and procedures that apply to functions also apply to scripts. For example, variables are declared the same way in both and functions are called the same way in both. There are also many differences. If we are describing a design facet that applies only to a script or only to a function, we will be careful to point that out. Otherwise, the rule applies equally to both. Types of Statements The following is a list of the types of statements that make up scripts. Each statement is like a part of speech in the JAWS script language, has a specific job to perform, and has certain rules associated with its use. Comments Comments are lines of text in your script file that are not executable statements. They are usually used to inform those reading the source file how the statements work. Comments may be placed on a line by themselves or at the end of a line. Begin a comment with a semicolon. Everything else on the line after it is treated as a comment. If you need multiple lines for comments, start each line with a semicolon. Includes The include statement tells the compiler to include the contents of another file along with this file, just as if that information had been typed into the main file at the point of the include statement. Hence, all of the information in the included file is accessible by the main file from the point of the include to the end of the main file. This means that all included files will be compiled along with the main file into one, large JSB file. Include files are separate files with common items that may be usable by a number of script files or which you may wish to keep separate for reasons of clarity or convenience. There are two types of include files: Header Files are designated by the extension JSH and contain either variable or constant declarations. Message Files are designated with the extension JSM and contain message statements with their assigned numbers. In versions of JAWS before 3.0, it was sometimes necessary to break macro files into pieces to avoid exceeding the maximum size allowed.