Ideas and specifications for JAWS for Windows Macro Files, August 22, 1995, Ted Henter We will present ideas/guidelines and examples of what and how we want the JFW macro files to be written. This is important information for anyone entering our "Macro Madness" contest or developing high-quality macro files on their own. These are ideas and suggestions, not cast in concrete, and suggestions for improvements and extra features are welcome. Generally speaking, your application macros should have customized logic for {Insert+F1} screen-sensitive help, {Insert+H} Hot Keys help, and {FocusChanged} event macros. In addition, {Text} and {HighlightedText} event macros may need modifying. Special macro keys to read information from the screen are always welcome. Further discussion and examples follow. Insert+F1, THE SCREEN SENSITIVE HELP: This is a very valuable feature for anyone learning Windows or a new Windows application. It should tell the user the type of window the active cursor in currently in. This helps him or her to decide what to do next, and what to do now with the current control. A "control" is simply another window, usually a "child window" that has the focus (or has the active cursor on it), like an edit field or list box. Below is an example of the Insert+F1 macro from our Program Manager macro file. It has extra comments added for clarity and education, the current macro in the PROGMAN.JMS file may differ due to improvements and additions. The comments are preceded by a ";". MacroBegin {Insert+F1} var ;start of variable definition area string TheType, String TheClass If (GetCurrentWindow ()) then ;window handle is not 0 ; first handle the unknown window types let TheType = GetWindowType(GetCurrentWindow()) if (TheType == "Unknown") then Let TheClass = GetWindowClass (GetCurrentWindow ()) ;start of custom or special window recognizing, ; check the class name for special names that you know ; get the special names with the Insert+F1 or Insert+F2 macros if (TheClass == "PmGroup") then SayString ("this is a group window in program manager") SayString ("it contains icons for launching applications") SayString ("use reeding keys to reed the icons") SayString ("or the first letter of the name to select it") SayString ("if the pc cursor is on then press enter to launch") SayString ("or click the mouse once to select or twice to launch") return ; exit this macro, your work is finished EndIf if ((TheClass == "pmhotkey") || (TheClass == "Ppmhotkey")) then SayString ("This is the Short cut key window ") SayString ("be careful, you will define a short cut key by pressing any letter key") SayString ("or most combinations of letter key and alt control or shift.") SayString ("for more information press F1 for help on Program Properties") SayString ("then tab to Short Cut Keys and press enter") return ; exit this macro, your are done EndIf ; add any additional checks here, ; use same syntax and logic as the examples above ; then you will arrive here if the current window was not handled above. ; if you have no special code for the current window, ; use the generic Insert+F2 macro performed below PerformMacroKey ({Insert+F2}) ; handle other unknown window types and classes else ;the window type was not unknown PerformMacroKey ({Insert+Control+f1}) ; known types, like edit or listbox endif ; end if type unknown else ; handle of current window was 0 PerformMacroKey ({Insert+F2}) ;handle = 0 EndIf MacroEnd You can see that the above macro uses two other pre-defined macros to take care of generic functions that will not change, i.e. the known window types like edit, combo box, scroll bar, etc., and the totally unknown window types. The Insert+F2 macro simply says the window type name, and then says and spells the window class. This is how you get to know the exact class names and spellings so you can write the special code that handles it, lie the "pmhotkey" window above. These techniques can be used for any window: instead of just saying and spelling the window name (big deal) it should recognize it and give the user some useful information. FOCUS CHANGED EVENT MACRO: The FocusChanged macro is probably the single most important macro in the JFW arsenal. It has the responsibility of speaking something appropriate each and every time the focus changes. What is a "focus change"? Every time a new application program is launched, every time a new dialog box appears or disappears, everytime a menu appears or disappears, or any time you tab from one control to another (from the edit field to a list box) that is a "focus change" and this macro gets "called" or "performed". If you look at {FocusChanged} in the DEF_INCL.JMS file you will see the default or generic logic for handling this focus change. It first tries to decide if there is a new application, if so then speak its name/title. If there is a new "real window" (one that has a title, like a dialog box) then speak it, as long as it is not the same as the application window, and it is not the same as the window that has the current focus. Then, no matter what, speak the window that does have the current focus. When you launch a new application, or just alt+tab to bring it into focus, you are likely to hear the application name, the "real" window name (perhaps the document name or name of the dialog box that is active), then the name of the "current window", the one that has the focus. It say all three things since that are "new". If you bring up the "file open" dialog box in Notepad you will only hear the name of the dialog box and the current control/child window, "file name edit". If you then tab to the list box you will only hear it say "list box", because neither the application nor the dialog box change. Then if you choose a file name it will say the name of the document (the new real window) and the name of the current control "edit". That is just a sample of how the default generic focus changed macro works. What about a special one to handle a special situation in your favorite application? Look towards the bottom of the focus changed macro in DEF_INCL.JMS. It performs the Insert+; macro to handle the last step, speaking the current control, the one with the focus. You can easily replace this macro in your own macro file designed for your application: any macros found in the default macro set are ignored if the same macro is found in the application-specific macro set. That is, to replace the default macro simply copy it into your application macro file and modify it. When you exit that application the default macro is still there and gains control again. Look at the c file for an example of a special Insert+; macro designed to handle the "short cut key" window in the Program Properties dialog (activate it through the File menu or Alt+Enter). An example of this macro with extra comments appears below. MacroBegin {Insert+;} ;this macro is performed by the Default focus Change macro ;no matter what, say the window currently with focus ; below handles all the known window types SayWindowTypeAndText (GlobalFocusWindow) ; now handle any special window types or classes ; special check for the hot-key section of the Properties dialog box, ; a nonstandard window type, it is not spoken by any of the above. if ((GetWindowClass (GlobalFocusWindow) == "pmhotkey") || (GetWindowClass (GlobalFocusWindow) == "Ppmhotkey")) then ; we are definitely on the hot key or short cut key window, ; first speak the "previous window" that contains the label/prompt SayWindow (GetPriorWindow (GlobalFocusWindow), 0) ; now say the window that contains the short cut key definition, if any SayWindow (GlobalFocusWindow, 0) ; now throw in a special warning for beginners If (GetVerbosity () == 0) Then SayString ("be careful or you will define a short cut key by pressing any letter key") SayString ("or most combinations of letter key and alt control or shift") endif ; end if verbosity set to beginner EndIf ; end if window class = hot key MacroEnd ; end of Insert+; macro Some applications will require extensive modifications to the focus changed macro, more than can be handled by the Insert+; macro. It can handle only the last step, a special control or window that is asking for a special action. Some applications use special, non-standard child windows or dialog boxes, we usually call them "real" windows because they have a name or title. In these cases more extensive changes are required, although similar logic can be used to provide similar, familiar performance. The Word for Window macro file, WINWORD.JMS, contains a FocusChanged macro with extensive modifications. First you will notice that is checks for a "SDM" real window or dialog box. This is a special, non-standard type of dialog box developed by Microsoft, bless their hearts. The SDM window also contains special controls, like edit fields and list boxes, that require special handling. Notice the macro functions that start with "SDM" that we developed for this situation. If you read further into this complex macro you will see a special check for the Spell Checker dialog, so it acts differently when an unrecognized word pops up. In Fact, this macro does nothing at all in this case, purposely keeping silent, allowing the {Text} event macro and the {Insert+W} macros to do all the talking. There is a good reason for this: This macro only gets performed whenever the focus changes, which happens only when you first start the spell checker. After the first word, the focus stays on this dialog box, it does not change, so the macro does not get called again until spell checking is over. Obviously this would not do, unless you have only one misspelled word in each of your documents. This type of behavior is quite common and must be taken into account when developing user friendly behavior through the JFW macro system. Notice that the Insert+W macro actually is designed to read the appropriate information from the spell checker dialog box: It reads "Not in Dictionary" followed by the word being spoken and spelled. Then it reads "Change to:" followed by the suggested word both spoken and spelled. If this is too much verbiage then you can set Verbosity to Intermediate or Advanced and hear just the pertinent data. This macro works fine after the words have appeared on the screen and you muster the energy to press the right keys. But us lazy computer users want it to speak/spell automatically, as soon as the words pop up. We must find a "event" that can be used to trigger the Insert+W macro at the appropriate time, and only at that time, once per unrecognized word. There are three event macros that might meet our needs. The FocusChanged macro gets called only once, so there would be no unwanted repeating of the data. Unfortunately it gets called only for the first word, not subsequent words, so it will not work in this case. The highlighted text event gets called at least once for each and every word that is unrecognized by the spell checker. But it usually gets called more than once per word with unpredictable data being written to the screen. If we called our Insert+W macro each time the Highlighted Text macro got called it would cause unwanted repeating of the information. The {Text} event macro also gets called several times each time a new word pops up in the spell checker: All the un-highlighted text that appears on the screen is written through this macro, so it must re-write and refresh the text in the document and the text in the dialog box many times. However, the text being written is predictable to some extent: Each time a new unrecognized word appears in the spell checker dialog box the phrase "Not in Dictionary:" is written to the screen through the Text event macro. Here is the "trigger" we need to perform our macro at least once and only once for each new unrecognized word. The key phrase may be written to the screen as part of the documents text, so we must be careful to trigger only when in the spell checker and only when that phrase is being written into the appropriate "control" or child window. The macro logic below, copied from the WINWORD.JMS macro file, {Text} event macro, handles the situation beautifully: ; first check to see if right spot in dialog box, if ((GlobalCurrentControl == 29) ; first control in dialog && (GlobalPrevRealName == "Spelling:") ; definitely spell checker && StringContains (buffer, "Not in Dictionary")) then ; proper event PerformMacroKey ({Insert+W}) ; read the desired text EndIf This specific logic does not handle the case when the string "Capitalization:" is displayed instead of "Not in Dictionary:". A simple addition to this logic would fix it. The variables named GlobalCurrentControl and GlobalPrevRealName are defined or set by the FocusChanged macro as part of its special logic to handle SDM dialog boxes. Since they are "Global" they can be used by any macro. You must be sure they are being set properly and currently/recently by a reliable macro if you intend to use these or other global variables. For example, if you are not in an SDM dialog, and not using our macro logic that sets the GlobalCurrentControl value, then you better not use it, it would contain old and probably unreliable data. INSERT+H HOT KEYS HELP The Insert+H macro should be customized for each application. The macro can determine which dialog box it is in, or other child window or situation, and give the user the appropriate information to help them out. Here we mainly want to resent the "Hot keys" that can be used, i.e. Alt+D for the Directories list box in the file open dialog. Determining these hot keys may need some sighted assistance, the letter to be used is underlined and appears in the prompt/title of the control or child window. In the future we will have a JFW macro function to find and read that underlined letter, but you will still have to write the hot key information to be used in this macro. Below is an excerpt from the Program Manager macros, PROGMAN.JMS. Refer to the other application macro files for more examples, like WINWORD.JMS. MacroBegin {Insert+H} ; hot keys for Program Manager ; Insert h text for Program Item Properties Dialog box. if (GetWindowName (GetRealWindow (GetFocus())) == "Program Item Properties") then SayString("Tab through the controls or use the following Windows keys") SayString("To get to description edit use Alt D") SayString("Description is the text label associated with program icons") SayString("To get to the program command line use Alt C") SayString("This is the execute command for the application") SayString("To select the working directory use Alt W") SayString("This is the default directory where files will be saved") SayString("To set up a Windows short cut key use Alt S") SayString("This will allow you to launch the program with a key combination") SayString("Use Alt H for Windows help and further explanation or escape to Cancel") Return EndIf ; Insert h text for Program Group Properties Dialog box. if (GetWindowName (GetRealWindow (GetFocus())) == "Program Group Properties") then SayString("Tab through the controls or use the following Windows keys") SayString("To get to description edit use Alt D") SayString("Description is the text label which appears on title line of the new group") SayString("To get to Group file use Alt G") SayString("Use Alt H for Windows help or escape to Cancel") Return EndIf ; Insert h text for Run dialog box. if (GetWindowName (GetRealWindow (GetFocus())) == "Run") then ; End of sample Insert+H macro code. INSERT+W WINDOWS HELP The Insert+W macro key is designed to give the user Windows related help as opposed to JAWS related help. An example from the default macros follows, you can also look for examples in other application macro files. MacroBegin {Insert+W} SayString("The Following windows keyboard commands can be useful") SayString("To jump between running programs, Press Control plus Escape") SayString("This will put you in the Task List of all running applications") SayString("Use the arrow keys or Type the first letter and Enter to Choose") SayString("To close a selected application, use Alt plus F4") SayString("To close a selected Group, use Control plus F4") SayString("You also close selected documents with Control plus F4") MacroEnd OTHER MACRO IDEAS: Some dialog boxes may need special help, like quick access to the "file type" being saved in the Save As dialog box, or the directory path that will be used if you type in only the file name. Look for examples of these macros on Insert+F3 or Insert+F4 (just because they are close to F1 and F2). Our current WINWORD.JMS file has an example of this. The tab key in a data base or other formatted screen usually moves to the next field. You probably want to hear the name of the field and the data in the edit area. Refer to the tab and Insert+tab macros in the ACTWIN2.JMS file (for Act for Windows). This technique will not always work. Act uses a separate window for the field name and the edit area, but not all programs do. We are working on other solutions for those programs. Refer to ACTWIN2.JMS for examples of the FindString macro. It is used to find a particular piece of information, like phone number or company name, and read it. FOR MORE INFORMATION: You may ask us questions about macros in several ways. The best way currently is the "GO JAWS" forum on Compuserv. Soon we will have an Internet site. Our BBS is 813-528-8903. Any of these are preferable to voice phone, it is too difficult to provide acurate technical information, and most of not all of this information should be shared.