; $Id: va508jaws.jss,v 1.171 2008/05/30 13:48:38 dlee Exp $ /* Data Communication for this framework is provided through hidden windows Written for the VA508 Script project Original scripts by: JMerrill Updated: June, 2007 - JAvila and DLee, for Freedom Scientific Updated: October, 2007 by DLee JAWS 7.1 and 8.0 The top line of this file should be preserved for CVS purposes The framework requires the jaws.SR and VA508JAWSDispatcher.exe files in addition to the FSAPI 1.0 com library in the shared folder of the JAWS program */ ; Default includes from JAWS include "HjConst.jsh" include "hjglobal.jsh" include "common.jsm" ; constants are differentiated by underscores between words Const ; Not used by any code in this file, but read by JAWS.SR from this file to determine if the script file should be updated with a newer version VA508_Script_Version = 1137, ; Maximum property cache lifespan in milliseconds VA508_Cache_Mils = 2000, ; 2 seconds ; iCacheHandling values for VA508getComponentProp() ; See that function for more about these constants. VA508_Cache_Use = 0, ; Use the cache but don't force it to update VA508_Cache_Update = 1, ; Update the cache, then use it VA508_Cache_Skip = 2, ; Don't touch the cache at all, just pull straight from Framework ; Constant used to indicate that the framework sent a null property value explicitly. ; When a cache property value is actually null, it means the framework did not send a value for that property at all. ; This means either there was an error (VA508_QueryCode_Error dataStatus bit set) ; or the property has no value in the framework (appropriate VA508_QueryCode_* dataStatus bit for the property NOT set). VA508_Cache_Explicit_Null = " ", ; Used by AutoStartEvent only.to get the msg id used to communicate with sendMessage VA508_Reg_Msg_ID = "VA 508 / Freedom Scientific - JAWS Communication Message ID", ; Used only by VA508EnsureInitialized VA508_DLL_Dispatcher_Hidden_Window_Class = "TfrmVA508JawsDispatcherHiddenWindow", VA508_DLL_Hidden_Main_Window_Class = "TfrmVA508HiddenJawsMainWindow", VA508_Message_Get_DLL_With_Focus = 1, ; Not used by any code. VA508_DLL_Dispatcher_Hidden_Window_Title = "VA 508 JAWS Dispatcher Window", VA508_DLL_Hidden_Data_Window_Class = "TfrmVA508HiddenJawsDataWindow", ; General data format = prefix : next window handle : data ; See VA508GetApplicationData() for more info on the data structure. VA508_DLL_Data_Window_Delim = ":", ; Next two used only by VA508GetStringValue. VA508_DLL_Data_Offset = "=", VA508_DLL_Data_Length = ",", ; Names of data structure elements, found in the data structure itself (gsVA508varData). ; Pass these to VA508GetStringValue() to get values by name. VA508_FieldName_Caption = "Caption", VA508_FieldName_Value = "Value", VA508_FieldName_Control_Type = "ControlType", VA508_FieldName_State = "State", VA508_FieldName_Instructions = "Instructions", VA508_FieldName_Item_Instructions = "ItemInstructions", VA508_FieldName_Data_Status = "DataStatus", ; Query bits to retrieve each of the above items except Data_Status. ; These are passed to VA508GetApplicationData(). ; iDataStatus comes back with the same flags to indicate what was actually retrieved. VA508_QueryCode_Caption = 0x00000001L, VA508_QueryCode_Value = 0x00000002L, VA508_QueryCode_Control_Type = 0x00000004L, VA508_QueryCode_State = 0x00000008L, VA508_QueryCode_Instructions = 0x00000010L, ;16 VA508_QueryCode_Item_Instructions = 0x00000020L, ;32 VA508_QueryCode_Data = 0x00000040L, ;64 ; Query code to retrieve all of the above at once. VA508_QueryCode_All = 0x0000007FL, ; 127 ; Extra bits that can be sent to the VA508ChangeEvent VA508_Data_Change_Event = 0x00001000L, ; 4096 VA508_DataItem_Change_Event = 0x00002000L, ;8192 ; If this comes back from a query, it means the sent hwnd was not in the framework's list of windows ; This can happen if ; - The hwnd is to another application, or ; - The hwnd is to a system-generated window, such as SysHeader32 under a ListView or Edit under an edit combo. VA508_QueryCode_Error = 0x00800000L, ; Keystroke constants Key_F4 = "F4", ; Key to close an open combo box. ksRightArrow = "RightArrow", ksLeftArrow = "LeftArrow", ; Window class constants wcComboLBox = "ComboLBox", ; Window class of a normal combo box's list window. wcTMaskEdit = "TMaskEdit", wcTTabControl = "TTabControl", wcTTabSheet = "TTabSheet", wcTUpDown = "TUpDown", wcTTreeView = "TTreeView", wcTStringGrid = "TStringGrid", wcTComboBox = "TComboBox", wcEdit = "Edit", ; ControlTypes ctSpinBox = "SpinBox", ; Window Styles window_style_tabsWithButtons = 0x100, ; MSAA constans ChildID_Self = 0, SELFLAG_TAKEFOCUS = 0x1L, SELFLAG_TAKESELECTION = 0x2L, ROLE_SYSTEM_PAGETABLIST = 0x3cL, ROLE_SYSTEM_PAGETAB = 0x25L, ROLE_SYSTEM_LIST = 0x21L, STATE_SYSTEM_SELECTED = 0x00000002L, STATE_SYSTEM_FOCUSED = 0x00000004L, STATE_SYSTEM_FOCUSABLE = 0x00100000L, STATE_SYSTEM_SELECTABLE = 0x00200000L, STATE_SYSTEM_CHECKED = 0x00000010L, ; Messages msgPage = "Page", msgNotSelected = "not selected", msgDataOnly = "data only", msgRowAndColumnNumbers = "row and column numbers", msgRowAndColumnHeaders = "row and column headers", msgRowAndColumnHeadersAndNumbers = "row and column headers and numbers", msgScriptSetName = "V A 508 Scripts", msgSpace = " ", msgLoadingSettings = "loading settings", msgPersonalSettingsSaved = "personal settings saved", msgPersonalSettingsNotSaved = "personal settings not saved", msgChecked = "checked", msgNull = "", msgBlank = "blank", msgLevel = "Level ", msgColumn = "Column", msgRow = "Row", ; Personalized settings hKey_GridSpeechMode = "GridSpeechMode", ; ini key name hKey_GridBrailleMode = "GridBrailleMode", ; ini key name jvToggleGridSpeechMode="|ToggleGridSpeechMode:Grid Speech", ; function name then shown description jvToggleGridBrailleMode="|ToggleGridBrailleMode:Grid Braille", ; function name then shown description Section_ControlTypes = "ControlTypes", Section_BrailleClasses = "BrailleClasses", ; General Constants string_delim = "|", gi_mag_draw_highlights = 1, ; for magic LB_COUNT = 0x018B, LB_getcursel = 0x0188, TVGN_CARET = 0x0009, TVGN_NEXT = 0x0001, TVGN_PREVIOUS = 0x0002, TVM_EDITLABELA = 0x110E, TVM_EDITLABELW = 0x1141, TVM_GETNEXTITEM = 0x110A Messages @msgPosMOfN %1 of %2 @@ @msgItemCount1 %1 item @@ @msgItemCount2 %1 items @@ EndMessages ; All global variables start with "g" and a lowercase letter indicating their type string, int, handle or boolean Globals ; True for debugging enabled (see autoStartEvent) int inDebugging, ; True for framework debugging int fwDebug, ; Property cache ; GetTickCount() value at the time of the last full cache update int giVA508cacheTick, ; Handle of window to which the cache now applies handle ghVA508cacheHwnd, ; Properties cached string gsVA508cacheCaption, string gsVA508cacheValue, string gsVA508cacheControlType, string gsVA508cacheState, string gsVA508cacheInstructions, string gsVA508cacheItemInstructions, int giVA508cacheDataStatus, ; Cache of grid data for string grids ; "valid" is not passed by the DLL; it is set (along with all the rest of these) by VA508GetGridData(). ; The rest are passed in a ^-delimited string from the DLL in the Value field. string gsVA508cacheGridColHdr, int giVA508cacheGridColNum, int giVA508cacheGridColCnt, string gsVA508cacheGridRowHdr, int giVA508cacheGridRowNum, int giVA508cacheGridRowCnt, string gsVA508cacheGridCellVal, int giVA508cacheGridCellNum, int giVA508cacheGridCellCnt, ; Handle of the window of the DLL for the application in focus. handle ghVA508DLLWindow, ; ID of registered VA508_Reg_Msg_ID Windows message. ; Sent to the dispatch window and DLL-specific windows, obtained by RegisterWindowMessage int giVA508messageID, ; True if the app-specific DLL link has not yet been established. ; This link is remade every time a VA508 app receives focus. int gbVA508needToLinkToDLL, string gsVA508TutorMessage, ; saves a global tutor message variable that will be announced by JAWS when getCustomTutorMessage is automatically called if the user settings ask for it int giVA508TutorReturnValue, handle ghVA508tutorWindow, ; handle of the window where there was a custom tutor message string gsVA508dataWindowTitle, int giVA508dataWindowTitleLen, ; Data field names and values for the last-queried control. string gsVA508varData, string gsVA508data, ; A flag to suppress speech at certain times. int gbVA508suppressEcho, int glbSuppressFocusChange, ; Custom braille types string glbsTable, string glbsTable2, ; Custom control types for speech string glbsTable3, string glbsTable4, ; personalized settings for how row and column headers are displayed in a table int giGridSpeechMode, int giGridBrailleMode, int giAppHasBeenLoaded, ; keeps track of if the app has been loaded before so we don't need to pull the ini settings for personalized grids ; Globals for ChangeEvent speaking int giSuppressCaption, ; use custom caption when event occurs but focus did not move int giSuppressControlType, int giSuppressState, int giSuppressValue, int giSuppressInstructions, int giSuppressItemInstructions, int giDidFocusChange, ; did the focus event fire after the changeEvent did? If so, scrap speaking anything becuase handleCustomWindows will announce it handle ghFromChangeEvent, ; handle of window that last called changeEvent int lt_suppressHighlight, int giCancelEvent, int giSpokeCellUnit, int giDebugMode ;************************************************************** ; Conversion functionality (cast of any type to any other type within reason) ;************************************************************** Variant Function VA508Cast (variant Value) return Value EndFunction ;************************************************************** ; Dodge for a stringSegmentCount anomaly in at least JAWS 8: ; If the string ends with the delimiter, the final null segment is not counted. ; This causes it to be counted. ;************************************************************** int function VA508stringSegmentCount(string s, string sDelim) var int segcount let segcount = stringSegmentCount(s, sDelim) if stringRight(s, 1) == sDelim then let segcount = segcount +1 endIf return segcount endFunction ;************************************************************** ; Classification of list types: lb=listbox, lv=listview, lx=extended-select, lm=multiselect. ;************************************************************** string function getListType(int typeCode) if typeCode == WT_ListView || typeCode == wt_listboxItem then return "lv" elif typeCode == WT_ListBox then return "lb" elif typeCode == WT_MultiSelect_ListBox then return "lm" elif typeCode == WT_ExtendedSelect_ListBox then return "lx" endIf ; Return non-null, or things like stringContains("lb lm lx", getListType(typeCode)) will return 1! return "--" endFunction ;************************************************************** ; Detection of special focus situations. ;************************************************************** int function isSpecialFocus(int includeAllDialogs) if menusActive() || userBufferIsActive() || inHJDialog() then return True endIf if includeAllDialogs && dialogActive() then return True endIf if (getWindowClass(getFocus()) == "#32771" && getObjectTypeCode(getFocus()) == WT_ListBoxItem) then ; This is the Alt+Tab window. return True endIf return False endFunction globals object goCountDict, string gsCountList ;************************************************************** ; Reports execution counts of functions/code blocks. ;************************************************************** Void function watchCount(string sKey) var int resetting, string sKey1 if !inDebugging then return endIf let resetting = False if sKey == "*" then let sKey = stringChopLeft(sKey, 1) let resetting = True endIf if !sKey || resetting then ; Internal or external call to speak and reset counts. var int i let i = 1 while i let sKey1 = stringSegment(gsCountList, "|", i+1) if sKey1 then if goCountDict(sKey1) then sayMessage(OT_Message, formatString("%1 %2", sKey1, goCountDict(sKey1))) dictSet(goCountDict, sKey1, 0) endIf let i = i +1 else let i = 0 ; exits loop endIf endWhile if !sKey then return endIf endIf ; External call to count. if stringLeft(gsCountList, 1) != "|" then let goCountDict = createObjectEx("Scripting.Dictionary", False) let gsCountList = "|" endIf if !goCountDict then return endIf dictDelta(goCountDict, sKey, 1) if !stringContains(gsCountList, "|"+sKey+"|") then let gsCountList = gsCountList +sKey +"|" endIf scheduleFunction("watchCount", 8) endFunction ;*************************************************************** ; Sends Message to hidden window, and retrieves result from window caption. ; Has build in retry attempts and timeout ;*************************************************************** int Function VA508SendMessage(Handle Window, int wParam, int lParam) var int Result, int working, int pendingCount, int pendingMax, int retryCount, int idx1, int idx2, int doSend, string header1, string header2, string value let Result = 0 if (Window != 0) then let working = TRUE let retryCount = 2 ; was 3 let pendingMax = 3 ; was 4 let doSend = TRUE let header1 = GetWindowName(Window) while working if doSend then ; This condition should avoid "Unknown function call to VA508GetApplicationData" messages on application shutdown. ; The idea is to stop trying to send messages when the application closes. ; Otherwise, this function delays even the AutoFinishEvent for several seconds. ; GetAppFileName includes an extension and getActiveConfiguration does not, ; but == stops at the end of the shortest string, so it works. ; [DGL, 2007-05-24] if getAppFileName() == getActiveConfiguration() && !InHJDialog () then SendMessage(Window, giVA508messageID, wParam, lParam) else return 0 endIf let doSend = FALSE let pendingCount = pendingMax endIf ; doSend let header2 = GetWindowName(Window) ; messageBox(getWindowName(StringToInt(StringSegment(header2, VA508_DLL_Data_Window_Delim, 3)))) ; ":" if StringCompare(header1, header2, TRUE) == 0 then ; they are equal ; beep() Delay(1,true) let pendingCount = pendingCount - 1 if pendingCount < 1 then let retryCount = retryCount - 1 if retryCount < 1 then let working = FALSE else let doSend = TRUE endIf endIf else ; headers are not equal so we have a new one let working = false let value = StringSegment(header2, VA508_DLL_Data_Window_Delim, 3) ; ":" let Result = StringToInt(value) endIf endWhile endIf ; is window handle valid return Result EndFunction ;************************************************************** ; Uses MSAA to return a window name up to 4 K long even though getWindowName stops at 255 characters. ; MSAA is only used when it seems necessary; getWindowName is used otherwise for efficiency. ;************************************************************** string Function Get4KName(handle hWnd) var string wName, string mName, int cutoffLen ; Window names can reach 255 characters, but we choose an earlier cutoff for safety. let cutoffLen = 240 let wName = GetWindowName(HWnd) if stringLength(wName) > cutoffLen || (hasTitleBar(hwnd) && stringLength(wName) == 0) then var int childID, object obj let obj = GetObjectFromEvent(HWnd, -4, 0, childID) ; -4 = ObjID_Client let mName = obj.accName(0) if stringLength(mName) > cutoffLen then return mName endIf endIf return wName EndFunction ;*********************************************************************** ; Gets a value by its name from the data structure saved in gsVA508varData and gsVA508data. ;*********************************************************************** String Function VA508GetStringValue(string VarName) var string Search, string Result, int idx, int idx2, int idx3, int max, int offset, int len let Result = "" let Search = VA508_DLL_Data_Window_Delim + VarName + VA508_DLL_Data_Offset ; something like ":Caption=" let idx = StringContains(gsVA508varData, Search) ; returns starting character position if idx > 0 then ; we found a match let max = idx + 50 ; just in case of bad data - prevents infinite loop lock up let idx = idx + StringLength(Search) let idx2 = idx+1 while (SubString(gsVA508varData, idx2, 1) != VA508_DLL_Data_Length) && (idx2 < max) let idx2 = idx2 + 1 endWhile let idx3 = idx2 + 1 while (SubString(gsVA508varData, idx3, 1) != VA508_DLL_Data_Window_Delim) && (idx2 < max) let idx3 = idx3 + 1 endWhile let offset = StringToInt(SubString(gsVA508varData, idx, idx2-idx)) + 1 let len = StringToInt(SubString(gsVA508varData, idx2+1, idx3-idx2-1)) if len > 0 then let Result = SubString(gsVA508data, offset, len) EndIf EndIf return Result EndFunction ;*********************************************************************** ; Retrieves one or more data items, as indicated by iQueryCode, for the given window. ; Data ends up in gsVA508varData (names/indices) and gsVA508data (values). ; hwnd is the handle of the window for which info is wanted ; iQueryCode indicates what is wanted (VA508_QueryCode_* constants) ; ;gsVA508dataWindowTitle = caption: ; caption:[next window handle]:varlen:var=offset,length:var=offset,len:data ; varlen = from first to last : ;*********************************************************************** globals string gsVA508GetAppDataDebugBuf Void Function VA508GetApplicationData(handle hwnd, int iQueryCode) watchCount("*appData") var handle hWindow, handle nullHandle, string sCaption, string sSubTitle, int idx, int len, string sData let gsVA508varData = "" let gsVA508data = "" let gsVA508GetAppDataDebugBuf = "" let sData= "" if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +formatString( "VA508GetApplicationData execution info:\13\10Invocation: handle %1, iQueryCode %2", intToString(hwnd), intToString(iQueryCode) ) endIf ; hWindow becomes handle of data window received from dll window let hWindow = VA508Cast(VA508SendMessage(ghVA508DLLWindow, hwnd, iQueryCode)) if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +formatString( "\13\10VA508SendMessage(%1, %2) returns %3", intToString(ghVA508DLLWindow), intToString(iQueryCode), intToString(hWindow) ) endIf ; if more than 4k or 255char windows are chained together and window contains next windows hwnd like a linked list while (hWindow != 0) && (hWindow != 1) let sCaption= Get4KName(hWindow) ; uses MSAA to get the name of the dll Window if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +formatString( "\13\10sCaption from window %1: '%2'", intToString(hWindow), sCaption ) endIf let hWindow = nullHandle ; Data window title and len are set in Initialization function let sSubTitle = SubString(sCaption,1,giVA508dataWindowTitleLen) if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +formatString( "\13\10sSubtitle from caption: '%1'", sSubtitle ) endIf if StringCompare(sSubTitle, gsVA508dataWindowTitle , TRUE) == 0 then ; just checking to make sure we have proper window if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +"\13\10Data window title recognized" endIf let sCaption= StringChopLeft(sCaption, giVA508dataWindowTitleLen) ; get everything after the title let idx = StringContains(sCaption, VA508_DLL_Data_Window_Delim) ; ":" if idx > 1 then ; see if we have a handle embedded in caption let hWindow = VA508Cast(StringToInt(SubString(sCaption, 1, idx-1))) if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +formatString( "\13\10Next window: %1", intToString(hWindow)) endIf else if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +"\13\10No next window" endIf endIf let sCaption = StringChopLeft(sCaption, idx) ; pull out everything after handle if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +formatString( "\13\10Caption text saved (idx %1): '%2'", intToString(idx), sCaption ) endIf let sData=sData+sCaption ; save what we pulled out into sData variable endIf EndWhile if StringLength(sData) > 0 then ; we have valid data let idx = StringContains(sData, VA508_DLL_Data_Window_Delim) ; ":" if idx > 1 then ; get data let len = StringToInt(SubString(sData, 1, idx-1)) ; length is first delimiter, length is length of vars, var length/pos, and data let sData = StringChopLeft(sData,idx-1) ; get data let gsVA508varData = StringLeft(sData,len) ; variable data ; MessageBox(gsVA508varData) ; contains position and length of each property :Caption=0,2: let gsVA508data = StringChopLeft(sData,len) ; actual data ; messageBox(gsva508data) endIf else if fwDebug then let gsVA508GetAppDataDebugBuf = gsVA508GetAppDataDebugBuf +"\13\10No data to return." endIf EndIf EndFunction ;*********************************************************************** ; this function is called from AutoStartEvent and AutoFinishEvent to reset all global variables for privacy reasons ;*********************************************************************** Void Function VA508ResetGlobals () let ghVA508DLLWindow = VA508cast(0) let gsVA508dataWindowTitle = "" let gsVA508tutorMessage = "" let ghVA508tutorWindow = VA508cast(0) let giVA508TutorReturnValue = 0 ; Clear the cache of framework data. VA508cacheUpdate(0) let gbVA508suppressEcho = FALSE endFunction ;*********************************************************************** Void Function VA508EnsureInitialized () var int InstanceID, handle DispatchWindow, string className if gbVA508needToLinkToDLL then ; CHECK FOR DLL LINK let DispatchWindow = FindTopLevelWindow(VA508_DLL_Dispatcher_Hidden_Window_Class, "") if DispatchWindow then ; CHECK FOR DISPATCH WINDOW let ghVA508DLLWindow = VA508Cast(VA508SendMessage(DispatchWindow, VA508_Message_Get_DLL_With_Focus, 0)) ; sayInteger(ghVA508DLLWindow) if (ghVA508DLLWindow != 0) && (ghVA508DLLWindow != 1) then ; CHECK FOR VALID DLL WINDOW HANDLE let className = GetWindowClass(ghVA508DLLWindow) If StringCompare(className, VA508_DLL_Hidden_Main_Window_Class, TRUE) == 0 then ; dll window and main window are one in the same ; the dll window contains the name of the data window let gsVA508dataWindowTitle = Get4KName(ghVA508DLLWindow) let gsVA508dataWindowTitle = StringSegment(gsVA508dataWindowTitle, VA508_DLL_Data_Window_Delim, 1) + VA508_DLL_Data_Window_Delim let giVA508dataWindowTitleLen = StringLength(gsVA508dataWindowTitle) ; sayString("linked") let gbVA508needToLinkToDLL = FALSE endIf ; check CLASS name endIf ; check DLL window handle endIf ; check for existence of DISPATCH window endIf ; only call if we need to link2DLL if gbVA508needToLinkToDLL then ; if not linked then clear and try again let ghVA508DLLWindow = VA508cast(0) ; keep this delay at 1 or first field may not function on startup ScheduleFunction("VA508EnsureInitialized",1) EndIf EndFunction ;*********************************************************************** ; Get a property value from the cache. ; iQueryCode: The VA508_QueryCode_* constant indicating the wanted property. ; sVal: On return, the property value or null. ; Returns 1 if the wanted property is found, 0 if not, and -1 on error. ; On a return of 1, sVal is the property value; otherwise, it is null. ;*********************************************************************** int function VA508cacheGetVal(int iQueryCode, string byRef sVal) let sVal = "" if giVA508cacheDataStatus & VA508_QueryCode_Error then return -1 elif !(giVA508cacheDataStatus & iQueryCode) then return 0 elif iQueryCode == VA508_QueryCode_Caption then let sVal = gsVA508cacheCaption elif iQueryCode == VA508_QueryCode_Value then let sVal = gsVA508cacheValue elif iQueryCode == VA508_QueryCode_Control_Type then let sVal = gsVA508cacheControlType elif iQueryCode == VA508_QueryCode_State then let sVal = gsVA508cacheState elif iQueryCode == VA508_QueryCode_Instructions then let sVal = gsVA508cacheInstructions elif iQueryCode == VA508_QueryCode_Item_Instructions then let sVal = gsVA508cacheItemInstructions else ; This should not happen in production and indicates a programming error. beep() return -1 endIf return 1 endFunction ;*********************************************************************** ; Update the property cache. Called only by VA508getComponentProp() and VA508 ResetGlobals ; hwnd: The window handle of interest, or 0 to clear the cache. ;*********************************************************************** Void Function VA508CacheUpdate (handle hwnd) var int iQueryCode, int iStatus, string sVal let iQueryCode = 0 if hwnd then let iQueryCode = VA508_QueryCode_All endIf ; Clear the cache first. let giVA508cacheTick = 0 let ghVA508cacheHwnd = VA508cast(0) let gsVA508cacheCaption = "" let gsVA508cacheValue = "" let gsVA508cacheControlType = "" let gsVA508cacheState = "" let gsVA508cacheInstructions = "" let gsVA508cacheItemInstructions = "" let giVA508cacheDataStatus = 0 ; Clear grid data also. VA508getGridData("",0) ; If we're just clearing the cache, we're done. if iQueryCode == 0 then return endIf ; Replace the just-cleared cache from the framework. ; VA508getApplicationData should have been called by the caller already. let iStatus = StringToInt(VA508GetStringValue(VA508_FieldName_Data_Status)) let giVA508cacheTick = getTickCount() let ghVA508cacheHwnd = hwnd let giVA508cacheDataStatus = iStatus if giVA508cacheDataStatus & VA508_QueryCode_Caption then let gsVA508cacheCaption = VA508GetStringValue(VA508_FieldName_Caption) endIf if giVA508cacheDataStatus & VA508_QueryCode_Value then let gsVA508cacheValue = VA508GetStringValue(VA508_FieldName_Value) ; Grid data shows up in Value, so only try to get it if there's something to get. VA508getGridData(gsVA508cacheValue, 1) endIf if giVA508cacheDataStatus & VA508_QueryCode_Control_Type then let gsVA508cacheControlType = VA508GetStringValue(VA508_FieldName_Control_Type) endIf if giVA508cacheDataStatus & VA508_QueryCode_State then let gsVA508cacheState = VA508GetStringValue(VA508_FieldName_State) endIf if giVA508cacheDataStatus & VA508_QueryCode_Instructions then let gsVA508cacheInstructions = VA508GetStringValue(VA508_FieldName_Instructions) endIf if giVA508cacheDataStatus & VA508_QueryCode_Item_Instructions then let gsVA508cacheItemInstructions = VA508GetStringValue(VA508_FieldName_Item_Instructions) endIf EndFunction ;*********************************************************************** ; Return the framework field name corresponding to the given query code. ;*********************************************************************** string function VA508FieldNameFromQueryCode(int iQueryCode) var string sFieldName if iQueryCode == VA508_QueryCode_Caption then let sFieldName = VA508_FieldName_Caption elif iQueryCode == VA508_QueryCode_Value then let sFieldName = VA508_FieldName_Value elif iQueryCode == VA508_QueryCode_Control_Type then let sFieldName = VA508_FieldName_Control_Type elif iQueryCode == VA508_QueryCode_State then let sFieldName = VA508_FieldName_State elif iQueryCode == VA508_QueryCode_Instructions then let sFieldName = VA508_FieldName_Instructions elif iQueryCode == VA508_QueryCode_Item_Instructions then let sFieldName = VA508_FieldName_Item_Instructions else ; This should not happen in production and indicates a programming error. beep() return 0 endIf return sFieldName endFunction ;*********************************************************************** ; Return the query code corresponding to the given framework field name. ;*********************************************************************** int function VA508QueryCodeFromFieldName(string sFieldName) var int iQueryCode if sFieldName == VA508_FieldName_Caption then let iQueryCode = VA508_QueryCode_Caption elif sFieldName == VA508_FieldName_Value then let iQueryCode = VA508_QueryCode_Value elif sFieldName == VA508_FieldName_Control_Type then let iQueryCode = VA508_QueryCode_Control_Type elif sFieldName == VA508_FieldName_State then let iQueryCode = VA508_QueryCode_State elif sFieldName == VA508_FieldName_Instructions then let iQueryCode = VA508_QueryCode_Instructions elif sFieldName == VA508_FieldName_Item_Instructions then let iQueryCode = VA508_QueryCode_Item_Instructions else ; This should not happen in production and indicates a programming error. beep() return 0 endIf return iQueryCode endFunction ;*********************************************************************** ; Gets a single component property immediately. ; hwnd: Window about which to ask. ; xProp: VA508_QueryCode_* or VA508_FieldName_* constant indicating or naming the wanted property. ; iCacheHandling: A VA508_Cache_* constant determining how to handle the property cache on this call. ; sVal: On return, the value of the property if found, otherwise undefined. ; Returns 1 if the property has a custom value, 0 if not, and -1 on error. ; iCacheHandling constant usage in detail: ; VA508_Cache_Use: ; If hwnd is the window cached already and the cache is not too old, pull the property from the cache and don't ask the framework. ; If hwnd is not the cached window or the cache is too old, rebuild the cache completely while getting the property. ; VA508_Cache_Update: Rebuild the cache unconditionally from the framework while getting the property. ; VA508_Cache_Skip: ; Get the property directly from the framework. ; If hwnd is the cached window, update the cache entry for the property in the process. ; Called by VA508SayData for Speech and controlPropGet for Braille ;*********************************************************************** int Function VA508GetComponentProp(handle hWnd, variant xProp, int iCacheHandling, string byRef sVal) Var string sProp, int iQueryCode, Int iDataStatus ; ***** Code from here to the next short set of stars can be called 40 or more times per second by Braille code. ; For efficiency, frequent callers should pass a query code, not a field name here. if stringToInt(xProp) then let iQueryCode = VA508cast(xProp) let sProp = "" ; Don't figure that one out unless we need it. else let sProp = VA508cast(xProp) let iQueryCode = VA508QueryCodeFromFieldName(sProp) endIf ; Optimize by far the most frequent call case. let sVal = "" if iCacheHandling == VA508_Cache_Use && hwnd == ghVA508cacheHwnd && getTickCount() -giVA508cacheTick < VA508_Cache_Mils then ; Use the cached value and don't call on the framework at all this time. watchCount("cacheUse") return VA508cacheGetVal(iQueryCode, sVal) ; ***** End of 40-time-per-second code block. elif iCacheHandling == VA508_Cache_Skip then ; Always go to the framework for these. We'll need the field name for that. if !sProp then let sProp = VA508FieldNameFromQueryCode(iQueryCode) endIf watchCount("cacheSkip") VA508GetApplicationData(hWnd, iQueryCode) let iDataStatus = StringToInt(VA508GetStringValue(VA508_FieldName_Data_Status)) ;"DataStatus" if iDataStatus & iQueryCode then let sVal = VA508GetStringValue(sProp) if hwnd == ghVA508cacheHwnd then ; A trick to re-use VA508cacheSetVals for this. ; Makes sure the dataStatus bit is set and sets the property value. VA508cacheSetVals(iQueryCode, iDataStatus, sVal, sVal, sVal, sVal, sVal, sVal) endIf return 1 elif iDataStatus & VA508_QueryCode_Error then ; Should not happen, but force the next call to update the cache if it does. if hwnd == ghVA508cacheHwnd then let giVA508cacheTick = 0 endIf return -1 else ; Property not handled by the framework. if hwnd == ghVA508cacheHwnd then if giVA508cacheDataStatus & iQueryCode then ; Property removed from framework (probably never happens). ; Remove the status bit from the cache as well. ; let giVA508cacheDataStatus = giVA508cacheDataStatus -iQueryCode endIf endIf return 0 endIf endIf ; All remaining use cases are rebuild-cache cases. ; Clear the cache first. VA508cacheUpdate(0) ; Get all properties from the framework. watchCount("cacheUpdate") VA508GetApplicationData(hWnd, VA508_QueryCode_All) ; Update all properties in the cache. VA508cacheUpdate(hwnd) ; Return the property sought in the first place. return VA508cacheGetVal(iQueryCode, sVal) EndFunction ;********************************************************************** ; Update the property cache when the framework sends an event indicating property changes. ; Called only from VA508ChangeEvent and getComponentProp with skip ; should update only items that have changed and getcomponentProp ;********************************************************************** Void Function VA508CacheSetVals (handle hwnd, int iDataStatus, string sCaption, string sValue, string sControlType, string sState, string sInstructions, string sItemInstructions) ; Don't mess with the cache if it doesn't apply to this window. if hwnd != ghVA508cacheHwnd then return endIf ; This is in case the framework suddenly decides to include a property not previously included by tossing it at us in an event call. ; This will probably never happen, but this line shouldn't hurt anything anyway. let giVA508cacheDataStatus = giVA508cacheDataStatus | iDataStatus ; Hand out property changes based on what we're told changed. if iDataStatus & VA508_QueryCode_Caption then let gsVA508cacheCaption = sCaption endIf if iDataStatus & VA508_QueryCode_Value then let gsVA508cacheValue = sValue endIf if iDataStatus & VA508_QueryCode_Control_Type then let gsVA508cacheControlType = sControlType endIf if iDataStatus & VA508_QueryCode_State then let gsVA508cacheState = sState endIf if iDataStatus & VA508_QueryCode_Instructions then let gsVA508cacheInstructions = sInstructions endIf if iDataStatus & VA508_QueryCode_Item_Instructions then let gsVA508cacheItemInstructions = sItemInstructions endIf EndFunction ;********************************************************************** ; Fills the VA508cacheGrid* globals. ; called by va508ChacheUpdate ; fromWhere: Hwnd of window to query (presumably a grid), or Value field already obtained from such a window. ; Returns True if this is a grid and False otherwise. ; Side effects: Modifies all g?VA508cacheGrid* globals. ; TODO: If a row or column header or cell value contains a ^, False will be returned and no grid data will be set. ;********************************************************************** int Function VA508GetGridData (variant fromWhere, int dontCheck) watchCount("grid") var int iResult, string sVal ; It's not a grid until we say it's a grid... let gsVA508cacheGridColHdr = "" let giVA508cacheGridColNum = 0 let giVA508cacheGridColCnt = 0 let gsVA508cacheGridRowHdr = "" let giVA508cacheGridRowNum = 0 let giVA508cacheGridRowCnt = 0 let giVA508cacheGridCellNum = 0 let giVA508cacheGridCellCnt = 0 let gsVA508cacheGridCellVal = "" if !fromWhere then return False endIf if stringToInt(fromWhere) && !stringContains(fromWhere, "^") && !dontCheck then ; A window handle. ; sayString("grid check") let iResult = VA508GetComponentProp(fromWhere, VA508_FieldName_Value, VA508_Cache_Skip, sVal) If iResult < 1 then return False endIf else ; A value property already obtained from somewhere. let sVal = fromWhere endIf if VA508stringSegmentCount(sVal, "^") != 9 then ; Got a value, but it's not grid data. return False endIf ; sayString("valid grid found by ggd") let gsVA508cacheGridColHdr = stringSegment(sVal, "^", 1) let giVA508cacheGridColNum = stringToInt(stringSegment(sVal, "^", 2)) let giVA508cacheGridColCnt = stringToInt(stringSegment(sVal, "^", 3)) let gsVA508cacheGridRowHdr = stringSegment(sVal, "^", 4) let giVA508cacheGridRowNum = stringToInt(stringSegment(sVal, "^", 5)) let giVA508cacheGridRowCnt = stringToInt(stringSegment(sVal, "^", 6)) ; The cell value is at the end of the string from the DLL. let giVA508cacheGridCellNum = stringToInt(stringSegment(sVal, "^", 7)) let giVA508cacheGridCellCnt = stringToInt(stringSegment(sVal, "^", 8)) let gsVA508cacheGridCellVal = stringSegment(sVal, "^", 9) return True EndFunction ;*********************************************************************** ; this function is called from handleCustomWindows and should get called whenever tab, insert+tab are pressed. It also may get called by sayLine and Braille functions ; Returns False if we should use default and True if this function announced the control and we don't need to do anything else ;*********************************************************************** int Function VA508SayData(handle hwnd) var int iResult, int iResultInstructions, int iResultValue, int iResultState, int bUseDefault, string Caption, string Value, string ControlType, string State, string Instructions, string ItemInstructions, string Text, handle tempHandle, int TypeCode, int subTypeCode, string StaticText, string position, string Grouping, string GroupingType, int special, string newControlType, int iDataStatus ; clear global variables, this should happen every time tab is pressed let gsVA508tutorMessage = "" let ghVA508tutorWindow = tempHandle ; which is now null let giVA508TutorReturnValue = 0 let special = FALSE if gbVA508needToLinkToDLL then return False endIf ; the assumption is made that if the return value is 0 or less, that "" will be returned and not garbage let iResult = VA508getComponentProp(hwnd, VA508_QueryCode_Caption, VA508_Cache_Update, Caption) if iResult >= 0 then ; Framework has nothing for this window, -1 means error, if 0 this passes let bUseDefault = (iResult <= 0) let iResult = VA508getComponentProp(hwnd, VA508_QueryCode_Value, VA508_Cache_Use, Value) let iResultValue = iResult let bUseDefault = bUseDefault && (iResult <= 0) let iResult = VA508getComponentProp(hwnd, VA508_QueryCode_Control_Type, VA508_Cache_Use, ControlType) let bUseDefault = bUseDefault && (iResult <= 0) let iResult = VA508getComponentProp(hwnd, VA508_QueryCode_State, VA508_Cache_Use, State) let iResultState = iResult let bUseDefault = bUseDefault && (iResult <= 0) let iResultInstructions = VA508getComponentProp(hwnd, VA508_QueryCode_Instructions, VA508_Cache_Use, Instructions) let iResult = VA508getComponentProp(hwnd, VA508_QueryCode_Item_Instructions, VA508_Cache_Use, ItemInstructions) else let bUseDefault = TRUE ; because caption had error we can assume we should use default unless a special case below is needed endif let TypeCode = GetWindowTypeCode(hWnd) if !typeCode then let typeCode = getObjectSubTypeCode() Endif if isSpinBox(hwnd) then ; standard delphi spinboxes are not announced correctly if VA508getComponentProp(hwnd, VA508_QueryCode_Control_Type, VA508_Cache_Use, ControlType) < 1 then let ControlType = ctSpinBox let special = true Endif ; check for presence of custom controlType elif TypeCode == wt_listbox || TypeCode == wt_listView then ; position in group is wrong for standard delphi list boxes let special = true elif getWindowClass(hwnd) == wcTTreeView then ; to remove unchecked from value let special = true elif getWindowClass(hwnd) == "TORComboEdit" || getWindowClass(hwnd) == "TORComboBox" then let special = True endif ; check for spinBox if bUseDefault == FALSE || special then if VA508getGridData(getFocus(),0) > 0 && isPcCursor() && !userBufferIsActive() then let value = getCurrentCellHeadersData() elif typeCode == wt_listBox || typecode == wt_listView then if iResultValue < 1 then ; the framework either return nothing or an error let value = getAccName() Endif if iResultState < 1 then ; the framework either return nothing or an error if !getObjectState() then let state = getAccState() endif Endif elif typeCode == wt_treeview then if iResultValue < 1 then let value = getAccValue() Endif ; add this state even if another state exists and no matter where the value came from let state = tvGetFocusItemExpandStateString (hwnd) + " " + state Endif ; end check for special control types if ControlType && ControlTypeFound(ControlType, newControlType) then let ControlType = newControlType EndIf ; end custom spoken control type sayControlEx(hwnd, Caption, ControlType, State, Grouping, GroupingType, Value, PositionInGroup(), StaticText) else ; use default return False EndIf ; use default? ;Say(ItemInstructions,OT_TUTOR) ; should always say item instructions if they exist? ; See JAWS 8's tutorialHelp.jss::sayTutorialHelp() for the rationale for using this SayUsingVoice call. ; TODO: This may speak in a few undesirable places depending on user settings. var int ot let ot = OT_Tutor if !shouldItemSpeak(OT_Tutor) && False then ; TODO: "False" above should be "code invoked from script", but I see no way to establish that. let ot = OT_Line endIf sayUsingVoice(VCTX_Message, itemInstructions, ot) If iResultInstructions > 0 then let ghVA508tutorWindow = hWnd let gsVA508tutorMessage = Instructions let giVA508TutorReturnValue = iResultInstructions EndIf ; instructions return True EndFunction ;*************************************************************************** ; updated to announce custom information for listview/listbox type controls ; spoken order for listbox and listview = value or name, state, then position ; the name of the control is not announced here for listboxes ; customType = caption,type,value,state ; checkbox = caption,type,state ; radioButton = caption, type,state ; button = caption,type, state (use default) ; link = caption, type, state ; comboBox = value, position ; edit = name, type, value, state (use default) ; spinner = name, type, value, state (use default) ; multiline edit = current line ; page tab = all page tabs ; list item = state, value, position ; tree item = value, state, position, ; listbox/listview = not possible ; treeview = not possible ; dialog = not possible ; grid = caption, type, value, state ; menu = use default ; slider = caption, type, value ; progressBar = caption, type, value ; groupbox = not possible ;*************************************************************************** Script SayLine() var int theTypeCode, string strVal, string strState, handle hwnd, string strControlType ;sayString("sayline") if !isPcCursor() || isSpecialFocus(False) then performScript sayLine() return Endif /* ; Update the cache VA508GetComponentProp(hwnd, VA508_FieldName_Value, VA508_Cache_Update, strVal) let hwnd = getFocus() Let TheTypeCode = GetWindowSubTypeCode (GetFocus ()) If !TheTypeCode then ; if any unknown typeCode then get the sub type code from MSAA Let TheTypeCode = GetObjectSubTypeCode () EndIf ; if we have a custom control type, then we really don't know how much or little information should be spoken, so say it all! if theTypeCode == wt_unknown && VA508GetComponentProp(hwnd, VA508_FieldName_Control_Type, VA508_Cache_Use, strControlType) > 0 then ; make assumption that anything that has a value has child objects, doesn't cover case where it isn't a list but has a custom state but not a custom value VA508SayData(hwnd) return Endif ; this only applies to list boxes if stringContains("lb lm lx", getListType(theTypeCode)) ; doesn't apply to listview, should pick up tree with custom value && !VA508GetGridData(hwnd) then ; grids should be handled differently though, so if value but grid don't use this code, use code in sayLine function ; BOTH value and state are custom if VA508GetComponentProp(hwnd, VA508_FieldName_Value, VA508_Cache_Use, strVal) > 0 && VA508GetComponentProp(hwnd, VA508_FieldName_State, VA508_Cache_Use, strState) > 0 then SayMessage (OT_LINE, strVal) SayMessage (OT_item_state, strState) ; custom expanded/collapsed states should be handled by the framework SayMessage (OT_POSITION, PositionInGroup ()) return Endif ; only VALUE is custom, use state from jaws if VA508GetComponentProp(hwnd, VA508_FieldName_Value, VA508_Cache_Use, strVal) > 0 && VA508GetComponentProp(hwnd, VA508_FieldName_State, VA508_Cache_Use, strState) < 1 then SayMessage (OT_line, strVal) If (theTypeCode == WT_TREEVIEW || theTypecode == WT_TREEVIEWITEM) then SayTVFocusItemExpandState (hwnd) ; getObjectState() doesn't return correct info for tree views Else SayMessage (ot_item_state,getObjectState()) endif SayMessage (OT_POSITION, PositionInGroup ()) return Endif ; only STATE is custom - current case for checklist box if VA508GetComponentProp(hwnd, VA508_FieldName_State, VA508_Cache_Use, strState) > 0 && VA508GetComponentProp(hwnd, VA508_FieldName_Value, VA508_Cache_Use, strVal) < 1 then Let strVal = GetAccName() SayMessage (OT_line, strVal) SayMessage (OT_item_State, strState) SayMessage (OT_POSITION, PositionInGroup ()) return EndIf ; only state Endif ; are we on a listbox or a custom control that has it's value set ; another special case for listboxes ; no custom state or value found but need to use msaa for listboxes ; for standard listboxes that JAWS has trouble announcing not selected for if stringContains("lb lm lx", getListType(theTypeCode)) then ; doesn't apply to listview, only listbox, should pick up tree with custom value say(getAccState(),ot_item_state) say(getAccName(),ot_selected_item) ; uses msaa for list item, falls back on getObjectValue() SayMessage (OT_POSITION, PositionInGroup ()) return Endif */ ; performScript sayLine() ; this function in default is likely to call the function sayLine below sayLine() EndScript ;***************************************************************************************** String Function tvGetFocusItemExpandStateString (handle hwnd) if tvGetFocusItemExpandState (hwnd) then return "opened" else return "closed" Endif EndFunction ;***************************************************************************************** ; also called by control+home and control+end in grid ; called by script sayLine for edits and buttons when sayLine command is pressed void Function SayLine(int iDrawHighlights) var int typeCode, int subTypeCode, handle hwnd, string strState, string strCaption, string strValue, string strControlType, string strPosition, int special ;sayString("sayline") if !isPCCursor() || isSpecialFocus(False) then return sayLine(iDrawHighlights) endIf let hwnd = getFocus() let typeCode = getWindowTypeCode(hwnd) ; sayInteger(typeCode) let subTypeCode = getWindowSubTypeCode(hwnd) if stringContains("lb lm lx", getListType(TypeCode)) then let special = true ; for listbox without customizations we need this for msaa names to be announced elif getObjectTypeCode() == wt_listboxItem then ; for list view let special = true ; for listview without customizations we need this for position in group to be spoken elif (getWindowClass(hwnd) == "TORComboEdit" || getWindowClass(hwnd) == "TORComboBox") && positionInGroup() then let special = True EndIf ; simple call to update the cache VA508GetComponentProp(getFocus(), VA508_FieldName_Control_Type, VA508_Cache_Update, strControlType) ; added jda 4-22-08 to make standard edit combos announce blank when empty and nothing else, also prevents speaking of caption on standard edit combos when sayLine is pressed if getWindowClass(hwnd) == wcEdit && getWindowClass(getParent(hwnd)) == wcTComboBox then let typeCode = wt_editCombo endif ; combo boxes should not have their name spoken if (typeCode == wt_edit && subTypeCode != wt_multiline_edit) ; also covers edit comboes, and spinboxes because they have a typeCode of edit || typeCode == wt_button then ; also handles edit & edit comboes ; sayInteger(subTypeCode) if VA508SayData(hwnd) then return else ; else fall through to regular sayline sayLine(iDrawHighlights) ; calls internal sayline function as default has no sayline function return endIf ; if we have a custom control type, then we really don't know how much or little information should be spoken, so say it all! elif TypeCode == wt_unknown && VA508GetComponentProp(hwnd, VA508_FieldName_Control_Type, VA508_Cache_Use, strControlType) > 0 then ; make assumption that anything that has a value has child objects, doesn't cover case where it isn't a list but has a custom state but not a custom value VA508SayData(hwnd) return elif VA508getGridData(getFocus(),0) > 0 then ; GRID say(getCurrentCellHeadersData(),ot_line) return elif VA508GetComponentProp(getFocus(), VA508_FieldName_Caption, VA508_Cache_Use, strCaption) < 1 && VA508GetComponentProp(getFocus(), VA508_FieldName_Control_Type, VA508_Cache_Use, strControlType) < 1 && VA508GetComponentProp(getFocus(), VA508_FieldName_Value, VA508_Cache_Use, strValue) < 1 && VA508GetComponentProp(getFocus(), VA508_FieldName_State, VA508_Cache_Use, strState) < 1 && !special then sayLine(iDrawHighlights) ; calls internal sayline function as default has no sayline function return elif subTypeCode == wt_multiline_Edit then sayLine(iDrawHighlights) ; calls internal sayline function as default has no sayline function return endIf ; now we have at least one custom property and it is stored in our local variable or we are special let strPosition = PositionInGroup() if getWindowClass(hwnd) == "TORComboEdit" then let typeCode = WT_EditCombo elif getWindowClass(hwnd) == "TORComboBox" then let typeCode = WT_ComboBox endIf ; now let's set everything from default if there wasn't a custom framework property if VA508GetComponentProp(getFocus(), VA508_FieldName_Caption, VA508_Cache_Use, strCaption) < 1 then ;let strCaption = getAccName() let strCaption = getObjectName() ; don't need to worry about child objects Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_Control_Type, VA508_Cache_Use, strControlType) < 1 then let strControlType = getObjectType() Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_Value, VA508_Cache_Use, strValue) < 1 then let strValue = getAccValue() Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_State, VA508_Cache_Use, strState) < 1 then let strState = getObjectState() Endif if typeCode == wt_treeview then let strState = tvGetFocusItemExpandStateString (hwnd) + " " + strState Endif ; caption is typically not announced here, so no custom caption is announced unless we think we are on a control with no children and no value ; ControlType is typically not announced here, so no custom control type is announced ; controls with custom values should be handled by sayLine script and thus are not handled here ; instructions and item instructions are typically not announced when sayLine is pressed that is why custom ones are not set here if typeCode == wt_checkbox || typeCode == wt_radiobutton then say(strCaption,ot_control_name) say(strControlType,ot_control_type) say(strState,ot_item_state) Elif typeCode == wt_comboBox || typeCode == WT_EditCombo then if !strValue then let strValue = " " endIf say(strValue,ot_line) ; jda added 4-22-08 if statement to prevent position in group from being announced when tor edit combo box is empty if !(strValue == " " && stringLength(strValue)) then say(strPosition,ot_position) Endif Elif typeCode == wt_slider || typeCode == wt_progressBar then say(strCaption,ot_control_name) say(strControlType,ot_control_type) say(strValue,ot_selected_item) elif stringContains("lb lm lx", getListType(typeCode)) then say(strValue,ot_selected_item) say(strState,ot_item_state) say(strPosition,ot_position) elif typecode == wt_listview then ; for list views say(strValue,ot_selected_item) say(strState,ot_item_state) say(strPosition,ot_position) elif typeCode == wt_treeview then ; no level needs to be announced here say(strValue,ot_selected_item) say(strState,ot_item_state) say(strPosition,ot_position) Else ; for anything else speak everything if VA508SayData(hwnd) then return else sayLine(iDrawHighlights) ; this should never get called, here as backup! endif Endif EndFunction ;**************************************** ; this function should replace sayObjectTypeAndText int Function HandleCustomWindows(handle hwnd) ; sayString(getWindowClass(hwnd)) if getWindowClass(hwnd) == wcTComboBox && getWindowClass(getFirstChild(hwnd)) == wcEdit then return True endif if glbSuppressFocusChange then let glbSuppressFocusChange = FALSE return true endif ; don't waste our time processing if hWnd == 0 || getWindowClass(hWnd) == "Invalid" then return true Endif if VA508SayData(hwnd) then ; sayString("custom") return true ; customizations were announced else ; sayString("use default") return false ; we are using default code in JAWS endIf EndFunction ; called first ;************************************************************************ void Function SayTutorialHelp (int iSubType, int flag) If (GetCurrentWindow() == ghVA508tutorWindow) then If giVA508TutorReturnValue > 0 then ; if we have a global tutor message announce it if gsVa508tutorMessage == " " && stringLength(gsva508tutorMessage) == 1 then return ; prevents blank from being announced when custom tutor message is " " endif Endif Endif SayTutorialHelp (iSubType, flag) EndFunction ;*********************************************************************** ; JAWS internally calls this function... in JAWS 7.1 and higher ;*********************************************************************** String Function GetCustomTutorMessage () If (GetCurrentWindow() == ghVA508tutorWindow) then If giVA508TutorReturnValue > 0 then ; if we have a global tutor message announce it Return gsVA508tutorMessage ; if " " then JAWS may announce blank when insert+tab is pressed EndIf EndIf Return getCustomTutorMessage() EndFunction ;********************************************************************** ; initialize variables and tell code to perform handshake with dispatch window. Reset any globals in case other application left them in memory which should not be the case though ;********************************************************************** Void Function AutoStartEvent () let inDebugging = False let fwDebug = False let giDebugMode = 0 if fileExists(getJAWSSettingsDirectory() +"\\debug.ini") then let inDebugging = True endIf if fileExists(getJAWSSettingsDirectory() +"\\fwdebug.ini") then let fwDebug = True endIf let gbVA508needToLinkToDLL = TRUE let giVA508messageID = RegisterWindowMessage(VA508_Reg_Msg_ID) VA508ResetGlobals() VA508EnsureInitialized() UpdateBrailleClasses() UpdateControlTypes() If !giAppHasBeenLoaded Then LoadPersonalSettings() ; load personal settings let giAppHasBeenLoaded=TRUE EndIf EndFunction ;********************************************************************** ; called when user alt+tabs out of application, resets all global variables and forces the application to re-initialize next time. This helps to ensure patient privacy as no patient data is left in hidden windows. ;********************************************************************** Void Function AutoFinishEvent() var object nullObject let gbVA508needToLinkToDLL = TRUE VA508ResetGlobals() EndFunction ;********************************************************************** ; called when JAWSKey+Q is pressed to announce script file settings that are loaded and module file name that is being used ;********************************************************************** Script ScriptFileName() ; one line of debugging code if !giDebugMode then ScriptAndAppNames(msgScriptSetName) Else ; start debug mode DisplayDebugData() Endif ; end debug mode EndScript ;*********************************************************************** ; added by jda 4-22-08 ;*********************************************************************** void Function DisplayDebugData() var string strCaption, string strControlType, string strValue, string strState, string strInstructions, string strItemInstructions, int iCustomInfo, string sDisplayText let sDisplayText = "" if VA508GetComponentProp(getFocus(), VA508_FieldName_Caption, VA508_Cache_Use, strCaption) > 0 then ;say("has custom name",ot_jaws_message) ;say(strCaption,ot_control_name) let sDisplayText = sDisplayText + "Custom Name: " + strCaption + "\n" let iCustomInfo = true Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_Control_Type, VA508_Cache_Use, strControlType) > 0 then ;say("has custom type",ot_jaws_message) ;say(strControlType,ot_control_type) let sDisplayText = sDisplayText + "Custom Type: " + strControlType + "\n" let iCustomInfo = true Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_Value, VA508_Cache_Use, strValue) > 0 then ;say("has custom value",ot_jaws_message) ;say(strValue,ot_selected_item) let sDisplayText = sDisplayText + "Custom Value: " + strValue + "\n" let iCustomInfo = true Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_State, VA508_Cache_Use, strState) > 0 then ;say("has custom state",ot_jaws_message) ;say(strState,ot_item_state) let sDisplayText = sDisplayText + "Custom State: " + strState + "\n" let iCustomInfo = true Endif if VA508GetComponentProp(getFocus(),VA508_FieldName_Instructions, VA508_Cache_Use, strInstructions) > 0 then ;say("has custom instructions",ot_jaws_message) ;say(strInstructions,ot_line) let sDisplayText = sDisplayText + "Custom Instructions: " + strInstructions + "\n" let iCustomInfo = true Endif if VA508GetComponentProp(getFocus(), VA508_FieldName_Item_Instructions, VA508_Cache_Use, strItemInstructions) > 0 then ;say("has custom item instructions",ot_jaws_message) ;say(strItemInstructions,ot_line) let sDisplayText = sDisplayText + "Custom Item Instructions: " + strItemInstructions + "\n" let iCustomInfo = true Endif if !iCustomInfo then ;say("No custom information found",ot_Jaws_message) let sDisplayText = "no custom information found\n" Endif va508getApplicationData(getFocus(),va508_queryCode_all) ;copyToClipboard("Status:" + VA508GetStringValue(va508_fieldname_data_status)+ " Data: " + gsva508data) let sDisplayText = sDisplayText + "Status:" + VA508GetStringValue(va508_fieldname_data_status)+ " Data: " + gsva508data + "\n" + "DLL handle: " + IntToString(ghVA508DLLWindow) + "\n" + "Need to link to DLL?: " + IntToString(gbVA508needToLinkToDLL) + "\n" + "Data Windows Title: " + gsVA508dataWindowTitle + "\n" ;sayMessage(ot_message,"data copied to clipboard") If UserBufferIsActive() then UserBufferDeActivate() Endif UserBufferClear() sayMessage(ot_user_buffer,sDisplayText) EndFunction ;********************************************************************** int function abs(int n) ; Absolute value if n < 0 then return 0-n endIf return n endFunction ;********************************************************************** ; Get position and count from TORComboBox windows. ;********************************************************************** int function getTorComboInfo(int byRef pos, int byRef count) var handle hwnd, object o, int childID let hwnd = getCurrentWindow() if !hwnd then return False endIf if getWindowClass(hwnd) == "TorComboEdit" then let hwnd = getParent(hwnd) endIf if getWindowClass(hwnd) != "TorComboBox" then return False endIf let o = getObjectFromEvent(hwnd, -4, 0, childID) ; -4 = ObjID_Client if !o then return False endIf let o = o.accNavigate(8, childID) ; 8 = NavDir_LastChild if !o then return False endIf if o.accRole(0) == 9 then ; 9 = Role_System_Window let o = o.accChild(-4) if !o then return False endIf endIf if o.accRole(0) != 33 ; 33 = Role_System_List && isPCCursor() && getWindowTypeCode(getFirstChild(getFocus())) == WT_Button then let hwnd = getFirstChild(findTopLevelWindow("TORDropPanel", "")) if getWindowClass(hwnd) != "TORListBox" || abs(getWindowLeft(hwnd) -getWindowLeft(getFocus())) > 5 || abs(getWindowTop(hwnd) -getWindowBottom(getFocus())) > 5 then return False endIf let o = getObjectFromEvent(hwnd, -4, 0, childID) ; -4 = ObjID_Client if !o then return False endIf if o.accRole(0) != 33 then ; 33 = Role_System_List return False endIf endIf let count = o.accChildCount +0 let pos = o.accSelection +0 return True endFunction ;********************************************************************** ; add proper speaking of position in group information for standard delphi listboxes, listviews already work correctly ; called by sayTreeViewLevel from activeItemChangedEvent String Function PositionInGroup () var handle hwnd, int hItem, int hOrigItem, int count, int count1, int pos, string str let hwnd = getFocus() let pos = 0 let count = 0 if getObjectTypeCode() == wt_listboxitem then ; this is actually for list views which return a type code of listbox item let pos = lvGetFocusItem(hwnd) let count = lvGetItemCount (hwnd) elif getObjectTypeCode() == wt_listbox then let pos = SendMessage(hwnd,LB_GETCURSEL,0,0)+1 let count = SendMessage(hwnd,LB_COUNT,0,0) elif getWindowClass(hwnd) == "TorComboEdit" || (getWindowClass(hwnd) == "TorComboBox" && !positionInGroup()) then if !getTorComboInfo(pos, count) then let pos = 0 let count = 0 endIf elif getObjectTypeCode() == wt_treeview && !(StringContains(StringLower(PositionInGroup()),"of") || StringContains(StringLower(PositionInGroup()),"item")) then ; sayString(positioninGroup()) ; use custom let hItem = sendMessage(hWnd,TVM_GETNEXTITEM,TVGN_CARET,0) if hItem then let hOrigItem = hItem let count = 0 while hItem != 0 && count < 200 ; should always go through once let hItem = sendMessage(hWnd,TVM_GETNEXTITEM,TVGN_PREVIOUS,hItem) let count = count + 1 EndWhile let hItem = hOrigItem let count1 = -1 while hItem != 0 && count1 < 200 ; should always go through once let hItem = sendMessage(hWnd,TVM_GETNEXTITEM,TVGN_NEXT,hItem) let count1 = count1 + 1 EndWhile let pos = count let count = count+count1 Endif ; if we a valid handle for the item endif ; if we have a valid position if count then if pos == 0 then if count == 1 then let str = formatString(msgItemCount1, intToString(count)) else let str = formatString(msgItemCount2, intToString(count)) endIf else let str = formatString(msgPosMOfN, intToString(pos), intToString(count)) endIf endIf if stringLength(str) then return str endIf return PositionInGroup() EndFunction ;********************************************************************** ;********************************************************************** Script Test() var string str ; get4kName(getCurrentWindow()) ; sayInteger(RegisterWindowMessage("VA 508 / Freedom Scientific - JAWS Communication Message ID")) EndScript ;********************************************************************** ; Test function for getting and speaking and/or displaying all DLL framework data for any window. ; Requires BX to be loaded. ; Usage: ; First select a window via the BX Window Navigation map. ; Then from inside or outside of BX, type BXQuickKey T (for Test) . ; is a number or number plus other keys that determine what to query for and speak or show, as follows: ; - 0 for query and speak all, ; - Ctrl+Shift+3 for query and show-all (in JAWS virtual viewer), ; - 1-7 for query and speak caption, value, controlType, state, instructions, itemInstructions, or data individually, ; - 8-9 to send query bit 8 or 9 and speak result, ; - Shift+0-9 to send one of query bits 10-19 and speak result, ; - Ctrl+0-9 to send one of query bits 20-29 and speak result, ; - Ctrl+Shift+0-2 to send one of query bits 30-32 and speak result, or ; - Ctrl+Shift+4-9 for query and show caption, value, controlType, state, instructions, or itemInstructions individually. ; Parameter n is set to 0-9 for keys 0-9, 10-19 for Shift+0-9, 20-29 for Ctrl+0-9, and 30-39 for Ctrl+Shift+0-9. ; This function calls VA508GetApplicationData and VA508GetGridData but does all other parsing internally. ;********************************************************************** void function bxTestNum(int n) var handle hwnd, int iQueryCode, string fmt, string varData, string data, string delim, string seg, string varName, int varStart, int varLen, string varVal, int i, string debugBuf, string buf /* let hwnd = VA508Cast(bxGetWindow()) if !hwnd then bxSayString("No window selected", "") return endIf */ let hwnd = getCurrentWindow() let fmt = "" if n >= 32 then let n = n -33 let fmt = fmt +"v" endIf if n then let iQueryCode = 1 << (n-1) else let iQueryCode = VA508_QueryCode_All endIf VA508GetApplicationData(hwnd, iQueryCode) let debugBuf = gsVA508GetAppDataDebugBuf let varData = gsVA508varData let data = gsVA508data let delim = stringLeft(varData, 1) let buf = "" let buf = buf +formatString( ;"VA508 data for window of class %1:\13\10Delimiter: '%2'\13\10", "VA508 data for window of class %1:\13\10", getWindowClass(hwnd), delim ) let i = 1 while i let i = i +1 let seg = stringSegment(varData, delim, i) if stringLength(seg) then let varName = stringSegment(seg, "=,", 1) let varVal = "" let varStart = stringToInt(stringSegment(seg, "=,", 2)) +1 let varLen = stringToInt(stringSegment(seg, "=,", 3)) if varStart && varLen then let varVal = substring(data, varStart, varLen) endIf if stringLength(varVal) then let buf = buf +formatString("\13\10%1: %2", varName, varVal) if varName == "value" then ; Include grid data where appropriate. if VA508getGridData(varVal,0) then let buf = buf +formatString( "\13\10Grid data:\13\10Column %1 (%2 of %3)\13\10Row %4 (%5 of %6)\13\10Cell %7 (%8 of %9)", gsVA508cacheGridColHdr, intToString(giVA508cacheGridColNum), intToString(giVA508cacheGridColCnt), gsVA508cacheGridRowHdr, intToString(giVA508cacheGridRowNum), intToString(giVA508cacheGridRowCnt), gsVA508cacheGridCellVal, intToString(giVA508cacheGridCellNum), intToString(giVA508cacheGridCellCnt)) endIf elif varName == "dataStatus" then ; Some of these bits are obsolete but can still be passed, so they are named here. ; Obsolete bits: CheckForStateChanges through ItemChangeSpeakValues, except for ItemChanged. ; [DGL, 2007-05-31] let buf = buf +formatString(" (%1)", VA508Cast(olStringFlags(stringToInt(varVal), "|Caption|Value|ControlType|State|Instructions|ItemInstructions|Data||CheckForStateChanges|CheckForItemChanges|GetOnlyIfStateChanged|GetOnlyIfItemChanged|StateChanged|ItemChanged|ItemChangeSpeakValues|||||||||Error")) ) endIf endIf else let i = 0 ; exits loop endIf endWhile if fwDebug then let buf = buf +formatString("\13\10\13\10VarData: %1\13\10Data: %2", varData, data) if debugBuf then let buf = buf +"\13\10\13\10" +debugBuf endIf endIf ; fwDebug bxSayString(buf, fmt) endFunction ;********************************************************************** ; Automatically close a combo box if one is open when tab or shift+tab is pressed ; Without this, tab and shift+tab in an open combo box do nothing. ;********************************************************************** void function autoCloseIfOpenCombo() var string class, handle hWnd let hWnd = getFocus() if getWindowClass(hWnd) == wcComboLBox then TypeKey(Key_F4) endIf endFunction ;********************************************************************** Script Tab() if isSpecialFocus(False) then autoCloseIfOpenCombo() endIf PerformScript Tab() EndScript ;********************************************************************** Script ShiftTab() if isSpecialFocus(False) then autoCloseIfOpenCombo() endIf PerformScript ShiftTab() EndScript ;********************************************************************** ; Avoids double speech in edit combo boxes. ;********************************************************************** Void Function ValueChangedEvent (handle hwnd, int objId, int childId, int nObjType, string sObjName, string sObjValue,int bIsFocusObject) ; A fix borrowed from JAWS 9... if nObjType == WT_Edit && getObjectSubtypeCode() == WT_EditCombo then ; This seems to happen at random; ; nObjType should normally be WT_EditCombo also. [DGL, 2007-10-03] let nObjType = WT_EditCombo endIf ValueChangedEvent (hwnd, objId, childId, nObjType, sObjName, sObjValue, bIsFocusObject) if nObjType == wt_editCombo && bIsFocusObject && sObjValue then ; we can assume it was spoken so suppress sayHighLightedText let gbVA508suppressEcho = true endIf EndFunction ;********************************************************************** ; Overwrite of default to prevent double speaking in edit comboes and other places ;********************************************************************** Void Function SayHighLightedText (handle hwnd, string buffer) if lt_suppressHighlight then scheduleFunction("lt_clearSuppressHighlight", 3) return endIf ; sayInteger(GetWindowSubTypeCode(hwnd)) ; prevent double speaking in open edit comboes if getWindowClass(getFocus()) == wcComboLBox then if getWindowSubTypeCode(hwnd) == wt_editcombo || getWindowClass(hwnd) == "TORComboEdit" then return endIf EndIf ; prevent double speaking in TORComboEdit boxes, which seem to stay open. if (hwnd == getFocus() || getWindowClass(hwnd) == "TORListBox") && getWindowClass(getFocus()) == "TORComboEdit" then return endIf ;sayString(getWindowClass(hwnd) +" " +getWindowClass(getFocus())) if getWindowClass(hwnd) == wcTMaskEdit then return Endif ; added to suppress echo when valueChangedEvent works in closed editComboes if gbVA508suppressEcho then let gbVA508suppressEcho = false return EndIf if getWindowClass(hwnd) == wcTStringGrid && hwnd != getFocus() then return ; prevent speaking of highlight in grid when focus is not in that control Endif SayHighLightedText (hwnd, buffer) EndFunction ;********************************************************************** ; Handles custom translations of types and states for output. ; Also completely defines the structure and location of translation tables. ; sTable: Braille for Braille translations. ; Other tables such as "Speech" could be defined if necessary. ; iQueryCode: Properties to look up in the table. ; %1 and %2 in sKeyFormat become initial Type and State values, respectively. ; sTypeVal and sStateVal: Initial values on entry, initial or translated as appropriate on exit. ; Returns True if anything was changed and False if not. ; ; Table location: All in .jcf (initially VA508APP.jcf). ; Table section names: [ Translations] (e.g., =Braille) ; Keys (LHS in file): [|...] [|...] ; where propname = e.g. ControlType or State. ; Examples: "ControlType checkbox" or "ControlType|State Checkbox|Checked" ; Values (RHS in file): [|...] ; Examples: "|lbx" or "|lbx|" ; Full example: ; [Braille Translations] ; ControlType|State checkListBox|checked=|lbx| ; makes the type "lbx" and the state "" for a checked listbox item. ; Called by controlPropGet ;********************************************************************** int function VA508TranslateProps(string sTable, int iQueryCode, string byRef sTypeVal, string byRef sStateVal) var string sFile, string sSect, string sKeyFormat1, string sKeyFormat2, string sKeyFormat, string sKey, string sTran, string sDelim, int i, int iChanged let iChanged = False ; Set file and section. let sFile = findJAWSSettingsFile(getActiveConfiguration() +".jcf") if !fileExists(sFile) then ; No file, no translations. return False endIf let sSect = formatString("%1 Translations", sTable) ; Set key by constructing it based on iQueryCode. let sKeyFormat1 = "" let sKeyFormat2 = "" if iQueryCode & VA508_QueryCode_Control_Type then let sKeyFormat1 = sKeyFormat1 +"|ControlType" let sKeyFormat2 = sKeyFormat2 +"|%1" endIf if iQueryCode & VA508_QueryCode_State then let sKeyFormat1 = sKeyFormat1 +"|State" let sKeyFormat2 = sKeyFormat2 +"|%2" endIf let sKeyFormat = stringChopLeft(sKeyFormat1, 1) +msgSpace +stringChopLeft(sKeyFormat2, 1) let sKey = formatString(sKeyFormat, sTypeVal, sStateVal) ; Look up the key and abort if not found. let sTran = iniReadString(sSect, sKey, "", sFile) if stringCompare(sTran, "", True) == 0 then return False endIf ; A translation was found; interpret it. let sDelim = stringLeft(sTran, 1) let i = 1 if stringContains(sKeyFormat, "%1") then ; There is a Type translation. let i = i +1 let sTypeVal = stringSegment(sTran, sDelim, i) let iChanged = True endIf if stringContains(sKeyFormat, "%2") then ; There is a State translation. let i = i +1 let sStateVal = stringSegment(sTran, sDelim, i) let iChanged = True endIf return iChanged endFunction ;********************************************************************** ; Handles all control property requests. ; Called only by BraillePropHelper ; Returns 1 when a property has a custom value, 0 when not, and below 0 on error. ; This function defines the accepted list of JAWS control property names. ; Most of them come from the names of the BrailleAddObject* functions built in to JAWS. ; sOrigin can be current, focus, or hwnd. ; whichProp is the name of the property sought. ; whichProp can end in .brl for a Braille-specific answer. ; sVal is the returned property value if the function returns 1, undefined otherwise. ;********************************************************************** int function controlpropGet(string sOrigin, string whichProp, int nSubtype, string byRef sVal) var handle hwnd, int iCacheHandling, int isBraille, int iResult ; sayString("control prop") ; Get hwnd, iCacheHandling, and nSubtype worked out. let iCacheHandling = VA508_Cache_Skip if sOrigin == "focus" || (sOrigin == "current" && isPCCursor()) then let hwnd = getFocus() let iCacheHandling = VA508_Cache_Use if !nSubtype then let nSubtype = getObjectSubtypeCode() endIf elif sOrigin == "current" then let hwnd = getCurrentWindow() if !nSubtype then let nSubtype = getObjectSubtypeCode() endIf elif sOrigin == "hwnd" then let hwnd = stringToHandle(stringChopLeft(sOrigin, 4)) if hwnd == getFocus() then let iCacheHandling = VA508_Cache_Use endIf if !nSubtype then let nSubtype = getWindowSubtypeCode(hwnd) endIf else ; Not supported at this time. return 0 endIf ; Now for isBraille and whichProp. let isBraille = False if stringRight(whichProp, 4) == ".brl" then let whichProp = stringChopRight(whichProp, 4) let isBraille = True endIf ; Check the framework for custom values. if whichProp == "Name" then return VA508GetComponentProp(hwnd, VA508_FieldName_Caption, iCacheHandling, sVal) elif whichProp == "Type" || whichProp == "State" then var int iTypeRet, string sTypeVal, int iStateRet, string sStateVal let iTypeRet = VA508GetComponentProp(hwnd, VA508_FieldName_Control_Type, iCacheHandling, sTypeVal) let iStateRet = VA508GetComponentProp(hwnd, VA508_FieldName_State, iCacheHandling, sStateVal) if isBraille then if iTypeRet > 0 && iStateRet > 0 then VA508TranslateProps("Braille", VA508_QueryCode_Control_Type +VA508_QueryCode_State, sTypeVal, sStateVal) endIf if iTypeRet > 0 then VA508TranslateProps("Braille", VA508_QueryCode_Control_Type, sTypeVal, sStateVal) endIf if iStateRet > 0 then VA508TranslateProps("Braille", VA508_QueryCode_State, sTypeVal, sStateVal) endIf endIf ; isBraille if whichProp == "Type" then let sVal = sTypeVal return iTypeRet else ; State let sVal = sStateVal return iStateRet endIf elif whichProp == "Value" then var int iValFound let iValFound = VA508GetComponentProp(hwnd, VA508_FieldName_Value, iCacheHandling, sVal) > 0 if iValFound > 0 && VA508GetGridData(sVal,0) then ; GRID if isBraille then let sVal = "" if giGridBrailleMode == 1 || giGridBrailleMode == 3 then let sVal = "r"+IntToString(giVA508CacheGridRowNum)+"c"+intToString(giVa508CacheGridcolNum)+msgSpace Endif if giGridBrailleMode == 1 || giGridBrailleMode == 2 then let sVal = sVal + gsVA508cacheGridRowHdr +msgSpace +gsVA508CacheGridColHdr +msgSpace endif if gsVA508CacheGridCellVal == "" then let gsVA508CacheGridCellVal = "-" Endif else ; NOT BRAILLE let sVal = "" endIf ; isBraille let sVal = sVal + gsVA508CacheGridCellVal ;always show cell return true elif nSubType == wt_progressBar then if iValFound < 1 then let sVal = getObjectValue() return true Endif endif return iValFound ; TODO: Many of the rest are not handled yet. elif whichProp == "ContainerName" then ; Generally a group box name. elif whichProp == "ContainerType" then ; Not sure how often this one gets used. elif whichProp == "Position" then ; e.g., "1 of 4" or "4 items" for lists and trees. elif whichProp == "DlgPageName" then ; Name of active tab. elif whichProp == "DlgText" then ; Dialog static text. elif whichProp == "ContextHelp" then ; TODO: This could be Instructions or Item_Instructions. elif whichProp == "Time" then ; This may not be used at all. elif whichProp == "Level" then ; Tree level. endIf return 0 ; no custom value for this property endFunction ; Braille Support ;********************************************************************** int function BraillePropHelper(string whichProp, int nSubtype) ; Logic for BrailleAddObject* functions to use. var int x, int y, int tc, int iResult, string sVal, int nWindowSubTypeCode let nWindowSubTypeCode = getWindowSubtypeCode(getFocus()) if nWindowSubTypeCode == wt_unknown then let nWindowSubTypeCode = getObjectSubTypeCode() endif if nWindowSubTypeCode != nSubtype then ; BrailleAddObject* function(s) called on a parent (e.g., dialog) control. ; We currently have no way to handle this, so let JAWS do it. return False endIf let iResult = controlpropGet("focus", whichProp +".brl", nSubType, sVal) if iResult > 0 then if whichProp != "name" then ; prevents cursor from being placed on label instead of value in edit fields let x = getCursorCol() let y = getCursorRow() Endif BrailleAddString(sVal, x,y, 0) return True endIf return False endFunction ;************************************************************************ int Function BrailleClassFound(string ControlType, int byref nType) var int i, int count let i = StringSegmentIndex (glbsTable, string_delim, StringLower(msgSpace+ControlType), true) if i then let nType = StringToInt(StringSegment(glbsTable2,string_delim,i)) return true endif return false EndFunction ;************************************************************************ int function BrailleCallbackObjectIdentify() var int nType, string controlType if isSpinBox(getCurrentWindow()) then return wt_spinbox Endif if VA508GetComponentProp(getCurrentWindow(),VA508_FieldName_Control_Type,VA508_Cache_Use,controlType) > 0 then let nType = 0 if BrailleClassFound(ControlType, nType) then return nType EndIf Endif return BrailleCallBackObjectIdentify() EndFunction ; added so spinbox sub type code can make nSubType code passed to BrailleAddObject functions ; what is returned in getWindowSubTypeCode must matach what is returned in BrailleCallBackObjectIdentify for the BraillePropHelper to show any custom information ;************************************************************************ int Function getWindowSubTypeCode (handle hwnd) var int nType, string ControlType, int cacheHandling ; sayString("get window sub type code") if isSpinBox(hwnd) then return wt_spinbox Endif if hwnd == getFocus() then let cacheHandling = VA508_Cache_Use else let cacheHandling = VA508_Cache_Skip endIf if VA508GetComponentProp(getCurrentWindow(),VA508_FieldName_Control_Type,cacheHandling,controlType) > 0 then if BrailleClassFound(ControlType, nType) then return nType EndIf Endif return getWindowSubTypeCode(hwnd) EndFunction ;************************************************************************ ; These are all the BrailleAddObject* functions internally recognized as of JAWS 8.0. int function BrailleAddObjectName(int nSubtype) return BraillePropHelper("Name", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectType(int nSubtype) return BraillePropHelper("Type", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectState(int nSubtype) return BraillePropHelper("State", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectValue(int nSubtype) return BraillePropHelper("Value", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectContainerName(int nSubtype) return BraillePropHelper("ContainerName", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectContainerType(int nSubtype) return BraillePropHelper("ContainerType", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectPosition(int nSubtype) return BraillePropHelper("Position", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectDlgPageName(int nSubtype) return BraillePropHelper("DlgPageName", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectDlgText(int nSubtype) return BraillePropHelper("DlgText", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectContextHelp(int nSubtype) return BraillePropHelper("ContextHelp", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectTime(int nSubtype) return BraillePropHelper("Time", nSubtype) endFunction ;************************************************************************ int function BrailleAddObjectLevel(int nSubtype) return BraillePropHelper("Level", nSubtype) endFunction ;********************************************************************** ; Allows JAWS ListView commands like Ctrl+JAWSKey+numbers to work with custom but functional ListView controls ; Only affects JAWS 7.1 and later. ;********************************************************************** int function isTrueListView(handle hwnd) var int result let result = isTrueListView(hwnd) if result then return result endIf if stringContains(stringLower(getWindowClass(hwnd)), "list") && lvGetNumOfColumns(hwnd) > 0 then return True endIf return False endFunction ;********************************************************************** ; Makes F2 edit the current tree node as a left-click does ;********************************************************************** Script f2editTreeNode () var int hItem, handle hWnd sayCurrentScriptKeyLabel() let hwnd = getFocus() if getWindowClass(hWnd) == wcTTreeView then let hItem = sendMessage(hWnd,TVM_GETNEXTITEM,TVGN_CARET,0) if hItem then sendMessage(hWnd,TVM_EDITLABELW,0,hitem) return Endif elif getWindowTypeCode(hwnd) == WT_TREEVIEW then ; we have a tree that's not a TTreeView then it might not have an api so use mouse saveCursor() JAWSCursor() saveCursor() routeJAWSToPC() leftMouseButton() return endIf typeCurrentScriptKey() EndScript ;********************************************************************** ; Called by JAWS.SR when things happen to a window in the active VA508 application. ; This is the only function/script in this file called externally. ; ControlTypes are only sent for custom types and are always sent regardless of whether the type changes, it is doubtful that a type will actually change ; This function gets called before focusChangedEvent, thus 3ms schedule functions were used ;********************************************************************** void function VA508ChangeEvent(handle hwnd, int iDataStatus, string sCaption, string sValue, string sControlType, string sState, string sInstructions, string sItemInstructions) ; sayString("called") if giCancelEvent == true then return Endif let ghFromChangeEvent = hwnd let giDidFocusChange = false ; used to ensure that we don't have double speaking when the event comes in before handleCustomWindows has a change to speak it if iDataStatus & VA508_Data_Change_Event then let iDataStatus = iDataStatus - VA508_Data_Change_Event ; not needed so let's remove Endif if iDataStatus & VA508_QueryCode_Control_Type then let iDataStatus = iDataStatus - VA508_QueryCode_Control_Type ; not needed Endif ; Update the cache so Braille will update immediately. VA508CacheSetVals(hwnd, iDataStatus, sCaption, sValue, sControlType, sState, sInstructions, sItemInstructions) BrailleRefresh() ;for an instant update rather than waiting up to 2 seconds to see the change, or in the case of braille viewer instead of waiting 30 seconds /* sayString(formatString( "Event %1 vals %2 %3 %4 %5 %6 %7", decToHex(iDataStatus), sCaption, sValue, sControlType, sState, sInstructions, sItemInstructions )) */ ; Don't speak changes to non-focused windows. if hwnd != getFocus() then if inDebugging then bxGauge(3, 0) Endif return endIf watchCount("change") ; was a grid cell changed? ; jda 9-14-07 if VA508GetGridData(sValue,0) && !giSpokeCellUnit then say(gsVA508cacheGridColHdr,ot_control_name) say(gsVA508cacheGridRowHdr,ot_control_name) if gsVA508cacheGridCellVal == "" then let gsVA508cacheGridCellVal = " " ; so blank will be spoken Endif say(gsVA508cacheGridCellVal,ot_line) say("column " + intToString(giVA508cacheGridColNum),ot_position) say("row " + intToString(giVA508cacheGridRowNum),ot_position) return Endif ; For some one-thing-only changes, just speak what changed. ; Since changes are spoken individually this might affect reading order preferences that users have set when arrow keys are used if iDataStatus & VA508_QueryCode_Caption && sCaption then let giSuppressCaption = true Endif ; this is always set and thus would always be announced, thus it is filtered out and should not get called if iDataStatus & VA508_QueryCode_Control_Type && sControlType then let giSuppressControlType = true endIf if iDataStatus & VA508_QueryCode_State && sState then let giSuppressState = true Endif if iDataStatus & VA508_QueryCode_Value && sValue && !VA508GetGridData(hwnd,0) then let giSuppressValue = true Endif if iDataStatus & VA508_QueryCode_Instructions && sInstructions then let giSuppressInstructions = true Endif if iDataStatus & VA508_QueryCode_Item_instructions && sItemInstructions then let giSuppressItemInstructions = true Endif ScheduleFunction("AnnounceEvent",3) endFunction ;********************************************************************************************************* ; Called by the Announce* functions below. ;********************************************************************************************************* Void Function AnnounceProp(int iQueryCode, int outputType, int byRef propGlobalFlag) var string prop if propGlobalFlag && giDidFocusChange == false then if VA508getComponentProp(ghFromChangeEvent, iQueryCode, VA508_Cache_Use, prop) > 0 then sayMessage(outputType, prop) Endif Endif let propGlobalFlag = false EndFunction ;********************************************************************************************************* ; All these are called by va508ChangeEvent ;********************************************************************************************************* Void Function AnnounceEvent() AnnounceProp(VA508_QueryCode_Caption, OT_Control_Name, giSuppressCaption) AnnounceProp(VA508_QueryCode_Control_Type, OT_Control_Type, giSuppressControlType) AnnounceProp(VA508_QueryCode_State, OT_Item_State, giSuppressState) AnnounceProp(VA508_QueryCode_Value, OT_Selected_Item, giSuppressValue) AnnounceProp(VA508_QueryCode_Instructions, OT_Tutor, giSuppressInstructions) AnnounceProp(VA508_QueryCode_Item_Instructions, OT_Tutor, giSuppressItemInstructions) endFunction ;********************************************************************************************************* ; overwrite of default to allow control+tab to be used to change tabs in Delphi applications Script NextDocumentWindow () ChangeDocumentWindow(1) EndScript ;********************************************************************************************************* ; overwrite of default to allow control_shift+tab to be used to change tabs in Delphi applications Script PreviousDocumentWindow () ChangeDocumentWindow(0) EndScript ;********************************************************************************************************* ; allows control tab when pressed on a child object of a page tab to announce the change in page ; allows control tab to announce the correct tab when focus in on page tab control and style is set to button or flat button ;********************************************************************************************************* Void Function ChangeDocumentWindow(int direction) var string pageName, handle hwnd if isSpecialFocus(False) then if direction == 1 then PerformScript NextDocumentWindow() elif direction == 0 then PerformScript PreviousDocumentWindow() Endif return endIf let hwnd = getFocus() ; store to see if page changed let PageName = getDialogPageName() ; cause tab to change if getWindowClass(hwnd) == wcTTabControl then ; this control doesn't natively support control+shift+tab if direction == 1 then typeKey(ksRightArrow) elif direction == 0 then typeKey(ksLeftArrow) endif elif getWindowClass(getParent(hwnd)) == wcTTabControl || getWindowClass(getParent(getParent(hwnd))) == wcTTabControl then ; this control doesn't natively support control+shift+tab SelectTab(direction) endif if direction == 1 then PerformScript NextDocumentWindow() elif direction == 0 then PerformScript PreviousDocumentWindow() Endif let hwnd = getFocus() ; have to get focus again because it may have changed??? ; announce change when focus is on special type of TPageControl tab with buttons if getObjectTypeCode() == wt_tabcontrol then if getWindowStyleBits(hwnd) & window_style_tabsWithButtons then ; tabs with buttons or flat buttons, control+tab doesn't work correctly here let glbSuppressFocusChange = true delay(1, True) SayControlEx (hwnd, getDialogPageName(), "", "", "", "", "", "", "") ScheduleFunction("ClearSuppressFocusChange",3) return endif ; announce change when focus is on child of page tab elif PageName != getDialogPageName() && getDialogPageName() then say(getDialogPageName()+msgSpace+MsgPage,ot_controL_name) endif EndFunction ;********************************************************************************************************* ; overwrite of default function to return proper page names in Delphi windows String Function GetDialogPageName() var string page, object o, int cid, handle hwnd let hwnd = getFocus() let page = getDialogPageName() if page == "" then if getWindowClass(getParent(hwnd)) == wcTTabSheet || getWindowClass(getParent(getParent(hwnd))) == wcTTabSheet then ; for TPageControl when child or granchild has focus if getWindowTypeCode(getParent(hwnd)) == wt_tabControl then let page = getWindowName(getParent(hwnd)) elif getWindowTypeCode(getParent(getParent(hwnd))) == wt_tabControl then let page = getWindowName(getParent(getParent(hwnd))) endif ; TTabControl not handled here elif getWindowClass(getParent(hwnd)) == wcTTabControl || getWindowClass(getParent(getParent(hwnd))) == wcTTabControl then ; forTTabControl when child or grandchild has focus let page = getSelectedTab() elif getObjectTypeCode() == wt_tabControl then ; for TTabControl and TPageControl ; getObjectName() can return the wrong name let o = getFocusObject(cid) if o then let page = o.accName(cid) endif endif endif return page EndFunction ; gets the selected tab from a TTabControl when a child control is in focus ;*************************************************************************** String Function getSelectedTab() var object o, int cid, int count if getWindowClass(getParent(getFocus())) == wcTTabControl then ; forTTabControl let o = getFocusObject(cid) ; full child control let count = 1 while o && count < 10 && o.accRole(childId_self) != role_system_pagetablist let o = o.accParent() ; window let count = count + 1 EndWhile if o && o.accRole(childId_self) == role_system_pagetablist then let count = 1 while count <= o.accChildCount() if o.accState(count) & state_system_selected then return o.accName(count) endif let count = count + 1 EndWhile endif Endif EndFunction ;*************************************************************************** ; 1 for next ; 0 for previous ; Used for TTabControl to activate a new tab using MSAA ;*************************************************************************** Void Function SelectTab(int direction) var object o, int cid, int childCount, int count ; msaa structure includes all control in page under page tab list.... troubling ; only works when groupbox or panel is not used if getWindowClass(getParent(getFocus())) == wcTTabControl then ; forTTabControl let o = getFocusObject(cid) let count = 1 ; find the page tab list while o && count < 10 && o.accRole(childId_self) != role_system_pagetablist let o = o.accParent() ; window let count = count + 1 EndWhile ; find the selected child if o && o.accRole(childId_self) == role_system_pagetablist then let childCount = o.accChildCount() let count = 1 while count <= o.accChildCount() if o.accState(count) & state_system_selected then let cid = count endif let count = count + 1 EndWhile ; set focus to the next or prior child if direction == 1 then if o.accRole(cid+1) != role_system_pagetab then let cid = 0 endif ; role page tab o.accSelect(selflag_takeSelection,cid+1) elif direction == 0 then if cid > 1 then o.accSelect(selflag_takeSelection,cid-1) else ; just sit on first tab endif Endif ; direction EndIf ; role page tab list Endif ; TTabControl EndFunction ;*************************************************************************** ; resets the global variable that suppress focus changes from being announced Void Function ClearSuppressFocusChange() let glbSuppressFocusChange = FALSE EndFunction ;*************************************************************************** int Function isSpinBox (handle hwnd) if getObjectTypeCode() == wt_edit && getWindowClass(getNextWindow(hwnd)) == wcTUpDown then return true endif return false EndFunction ;*************************************************************************** ; get value for element from framework, falls back on getObjectValue() String Function getValue() var string value if VA508GetComponentProp(getFocus(),VA508_FieldName_Value,VA508_Cache_Update,value) <= 0 then ; no custom property returned let value = getObjectValue() endif return value ; could be null from either getObjectValue or from framework if it was set to null on purpose EndFunction ;*************************************************************************** ; updated for grid support int function moveByLine(int forward) var handle hwnd let hwnd = getFocus() if isPCCursor() && !isSpecialFocus(False) then if isSpinBox(hwnd) || getWindowClass(hwnd) == "TORComboEdit" || getWindowClass(hwnd) == "TORComboBox" then if forward then nextLine() else priorLine() endIf if isSpinBox(hwnd) || getWindowClass(hwnd) == "TORComboEdit" then say(getValue(),ot_selected_item) endIf return True elif SpeakCellUnit("downArrow") then return True endIf endIf return False endFunction Script SayNextLine() if !moveByLine(True) then performScript sayNextLine() endIf EndScript Script SayPriorLine() if !moveByLine(False) then performScript sayPriorLine() endIf EndScript ;*************************************************************************** ; updated for grid support Script SayNextCharacter() if SpeakCellUnit("rightArrow") then return Endif performScript sayNextCharacter() EndScript ;*************************************************************************** ; updated to allow correct announcement in grids ;*************************************************************************** Script SayPriorCharacter() if SpeakCellUnit("leftArrow") then return Endif performScript sayPriorCharacter() EndScript ;****************************************************************** function clearSpokeCellUnit() let giSpokeCellUnit = false EndFunction ;****************************************************************** ; Speaks the aspects of a cell that are different from the last cell visited int Function SpeakCellUnit(string str) var string strVal, string buildString ;sayString("speak cell unit") let giSpokeCellUnit = true scheduleFunction("clearSpokeCellUnit",2) ; prevent double speaking by changeEvent if !isPCCursor() || isSpecialFocus(False) then return 0 elif VA508getGridData(getFocus(),0) > 0 then ; IN GRID TypeKey(str) delay(1, True) ; Update the cache. VA508GetComponentProp(getFocus(), VA508_FieldName_Value, VA508_Cache_Update, strVal) VA508GetGridData(strVal,0) ; have to call here to refresh view when Braille is not being used if gsVA508cacheGridCellVal == "" then let gsVA508cacheGridCellVal = " " ; so it will say blank with ot_line Endif if !giGridSpeechMode then say(gsVA508cacheGridCellVal,ot_line) ; DATA ONLY return 1 elif str == "leftArrow" || str == "rightArrow" || str == "home" || str == "end" then ; COLUMN CHANGE if giGridSpeechMode == 1 || giGridSpeechMode == 2 then ; don't use ot_line here incase it is blank we dont' want blank spoken sayUsingVoice(VCTX_MESSAGE,gsVA508cacheGridColHdr,ot_control_name) Endif say(gsVA508cacheGridCellVal,ot_line) if giGridSpeechMode == 1 || giGridSpeechMode == 3 then say("column " + intToString(giVA508cacheGridColNum),ot_position) Endif return 1 elif str == "upArrow" || str == "downArrow" || str == "pageUp" || str == "pageDown" then ; Row change if giGridSpeechMode == 1 || giGridSpeechMode == 2 then ; don't use ot_line here incase it is blank we dont' want blank spoken sayUsingVoice(VCTX_MESSAGE,gsVA508cacheGridRowHdr,ot_control_name) Endif say(gsVA508cacheGridCellVal,ot_line) if giGridSpeechMode == 1 || giGridSpeechMode == 3 then say("row " + intToString(giVA508cacheGridRowNum),ot_position) Endif return 1 endif ; IN TAB CONTROL elif getObjectTypeCode() == wt_tabcontrol && getWindowStyleBits(getCurrentWindow()) & window_style_tabsWithButtons then ; tabs with buttons or flat buttons let glbSuppressFocusChange = true TypeKey(str) delay(1, True) sayControlEx(getCurrentWindow(), getObjectName(), getObjectSubtype(), getAccState()) ScheduleFunction("ClearSuppressFocusChange",3) return 1 endif return 0 EndFunction ;****************************************************************** ; returns the full string of col number, row number, column name, row name, and cell data for the current table cell ; used by sayLine, tab, shift+tab, insert+tab and control+home, and control+end string Function getCurrentCellHeadersData () var string str VA508GetGridData(getFocus(),0) ; have to call here to refresh view when Braille is not being used let str = "column" + msgSpace + IntToString(giVA508CacheGridColNum) + "row" + msgSpace + IntToString(giVA508CacheGridRowNum) + msgSpace let str = str + gsVA508cacheGridColHdr + msgSpace + gsVA508cacheGridRowHdr + msgSpace + gsVA508cacheGridCellVal return str EndFunction ;home and end in grid ;********************************************************************* ; updated for grid support Script JAWSHome() if SpeakCellUnit("home") then return Endif performScript JAWSHome() EndScript ;********************************************************************* ; updated for grid support Script JAWSEnd() if SpeakCellUnit("end") then return Endif performScript JAWSEnd() EndScript ;********************************************************************* ; updated for grid support Script JAWSPageDown() if SpeakCellUnit("pageDown") then return Endif performScript JAWSPageDown() EndScript ;********************************************************************* ; updated for grid support Script JAWSPageUp() if SpeakCellUnit("pageUp") then return Endif performScript JAWSPageUp() EndScript ;********************************************************************* Int Function UpdateBrailleClasses() var string sFile, string sTable, int i, int count let glbsTable = "" let glbsTable2 = "" let sFile = findJAWSSettingsFile(getActiveConfiguration() +".jcf") ; get applicatio specific jcf file if !fileExists(sFile) then ; No file, no translations. return False endIf ; file exists ; this line adds a space at the beginning of each item in the list that is returned because of a bug in StringSegmentIndex let glbsTable = msgSpace + StringReplaceSubStrings(StringLower(IniReadSectionKeys (section_BrailleClasses, sFile)),string_delim, string_delim+MsgSpace) ; msgSpace need for first item to match let count = VA508StringSegmentCount (glbsTable, string_delim) let i = 1 while i <= count let glbsTable2 = glbsTable2 + IntToString(IniReadInteger (section_BrailleClasses, StringChopLeft(StringSegment(glbsTable,string_delim,count),1), 0, sFile)) + string_delim let i = i + 1 endWhile if glbsTable2 then let glbsTable2 = stringChopRight (glbsTable2, 1) Endif return True EndFunction ;***************************************************************************************************** Int Function UpdateControlTypes() var string sFile, string sTable, int i, int count let glbsTable3 = "" let glbsTable4 = "" let sFile = findJAWSSettingsFile(getActiveConfiguration() +".jcf") if !fileExists(sFile) then ; No file, no translations. return False endIf let glbsTable3 = msgSpace + StringReplaceSubStrings(StringLower(IniReadSectionKeys (section_ControlTypes, sFile)),string_delim, string_delim+msgSpace) let count = VA508StringSegmentCount (glbsTable3, string_delim) let i = 1 while i <= count let glbsTable4 = glbsTable4 + IniReadString (section_ControlTypes, StringChopLeft(StringSegment(glbsTable3,string_delim,count),1), msgNull, sFile) + string_delim let i = i + 1 endWhile if glbsTable4 then let glbsTable4 = stringChopRight (glbsTable4, 1) Endif return True EndFunction ;************************************************************************ int Function ControlTypeFound (string ControlType, string ByRef newControlType) var int i, int count let i = StringSegmentIndex (glbsTable3, string_delim, StringLower(msgSpace+ControlType), true) if i then let newControlType = StringSegment(glbsTable4,string_delim,i) return true endif return false EndFunction /* Start - Personal settings */ ;******************************************************************************************* ; called by JAWS verbosity dialog to flip or return status String Function ToggleGridSpeechMode (int iRetVal) If not iRetVal Then ; flip only if space pressed Let giGridSpeechMode = giGridSpeechMode + 1 if giGridSpeechMode > 3 then let giGridSpeechMode = 0 Endif EndIf ; update it ; return state, used to build visual list of current settings If giGridSpeechMode == 1 Then return msgRowAndColumnHeadersAndNumbers elif giGridSpeechMode == 2 then return msgRowAndColumnHeaders elif giGridSpeechMode == 3 then return msgRowAndColumnNumbers Else ; 0 return msgDataOnly EndIf EndFunction ;******************************************************************************************* ; called by JAWS verbosity dialog to flip or return status String Function ToggleGridBrailleMode (int iRetVal) If not iRetVal Then ; flip only if space pressed Let giGridBrailleMode = giGridBrailleMode + 1 if giGridBrailleMode > 3 then let giGridBrailleMode = 0 Endif EndIf ; update it ; return state If giGridBrailleMode == 1 Then return msgRowAndColumnHeadersAndNumbers elif giGridBrailleMode == 2 then return msgRowAndColumnHeaders elif giGridBrailleMode == 3 then return msgRowAndColumnNumbers Else ; 0 return msgDataOnly EndIf EndFunction ;******************************************************************************************* Void Function LoadPersonalSettings () var string sFile ; Load personal preferences ; SayUsingVoice(VCTX_MESSAGE,msgLoadingSettings,OT_USER_REQUESTED_INFORMATION) let sFile = findJAWSSettingsFile(getActiveConfiguration() +".jsi") let giGridSpeechMode = IniReadInteger (Section_Options, hKey_GridSpeechMode, 1, sFile) let giGridBrailleMode = IniReadInteger (Section_Options, hKey_GridBrailleMode, 1, sFile) EndFunction ;******************************************************************************************* Int Function SavePersonalSettings () ; save personal preferences Var int iResult, string sFile let sFile = findJAWSSettingsFile(getActiveConfiguration() +".jsi") let iResult =IniWriteInteger (Section_Options, hKey_GridSpeechMode, giGridSpeechMode, sFile) let iResult = IResult | IniWriteInteger (Section_Options, hKey_GridBrailleMode, giGridBrailleMode, sFile) return iResult EndFunction ;******************************************************************************************* ; modified to include personalized settings for automatically speaking buddies as the sign in and incoming messages Script AdjustJAWSVerbosity () Var int iPrevGridSpeechMode, int iPrevGridBrailleMode, string sList ; save the current values let iPrevGridSpeechMode = giGridSpeechMode let iPrevGridBrailleMode = giGridBrailleMode let sList=jvToggleGridSpeechMode + jvToggleGridBrailleMode JAWSVerbosityCore (sList) ; call default ; If any values changed, save them... If iPrevGridSpeechMode != giGridSpeechMode || iPrevGridBrailleMode != giGridBrailleMode then If savePersonalSettings() then SayUsingVoice(VCTX_MESSAGE, msgPersonalSettingsSaved,OT_STATUS) Else SayFormattedMessage(ot_error, msgPersonalSettingsNotSaved) EndIf EndIf EndScript /* End - Personal settings */ ;*************************************************************************************** ; getObjectName() is often wrong and this can be used to get the real msaa name as long as the msaa cache is up-to-date in JAWS ; if no msaa name is returned then fall back on getObjectName and getObjectValue which use other methods in addition to MSAA to determine the name or value (name of list item) ; called on listboxes and listviews ;*************************************************************************************** string Function getAccName () var object o, int cid, string str let o = getFocusObject(cid) if o then if getObjectTypeCode() == wt_listbox || getObjectTypeCode() == wt_listview || getObjectTypeCode() == wt_listboxItem then let str = o.accName(cid) Else let str = getObjectName() Endif if o.accRole(cid) == role_system_list then ; we are on parent not simple child, should cover listView and listbox let str = o.accName(1) ; announce first item if nothing is focused Endif Endif ; object exists? if !str then if getObjectTypeCode() == wt_listbox || getObjectTypeCode() == wt_listview || getObjectTypeCode() == wt_listboxItem then let str = getObjectValue() else let str = getObjectName() EndIf Endif return str EndFunction ;*************************************************************************** string Function getAccValue () var object o, int cid, string str let o = getFocusObject(cid) if o then if getObjectTypeCode() == wt_listbox || getObjectTypeCode() == wt_listview || getObjectTypeCode() == wt_listboxItem || getObjectTypeCode() == wt_treeview then let str = o.accName(cid) else ; let str = o.accValue(cid) let str = getObjectValue() Endif if o.accRole(cid) == role_system_list then ; we are on parent not simple child, should cover listView and listbox let str = o.accName(1) ; announce first item if nothing is focused Endif Endif ; object exists? if !str then let str = getObjectValue() Endif return str EndFunction ;*************************************************************************************** ; currently only returns checked and/or not selected ; getObjectName() is often wrong and this can be used to get the real msaa name as long as the msaa cache is up-to-date in JAWS string Function getAccState () var object o, int cid, int ret, string str let o = getFocusObject(cid) if o then let ret = o.accState(cid) EndIf if ret & state_system_focused && ret & state_system_selectable && !(ret & state_system_selected) then let str = msgNotSelected endif if ret & state_system_checked then let str = str + msgSpace + msgChecked Endif return str EndFunction ;**************************************************************************************************** ; use to overwrite default speech for itmes when arrow keys are used and custom properties exist ; for listbox when focus changes within object ; arrow, home, end in listbox ; arrows, home, end in listview ;**************************************************************************************************** Void Function sayObjectActiveItem(int speakPositionInfo) var string strValue, string strState, int iResultValue, int iResultState ; sayString("sayObjectActiveItem") if getObjectTypeCode() == wt_tabcontrol && getWindowStyleBits(getCurrentWindow()) & window_style_tabsWithButtons then ; tabs with buttons or flat buttons say(getAccState(),ot_item_state) ; Makes JAWS say "not selected" when a tab is in focus but not selected (possible on this type of tab control) endif if stringContains("lb lm lx lv", getListType(getObjectSubtypeCode())) then let iResultValue = VA508GetComponentProp(getFocus(), VA508_FieldName_Value, VA508_Cache_Update, strValue) let iResultState = VA508GetComponentProp(getFocus(), VA508_FieldName_State, VA508_Cache_Use, strState) if iResultValue > 0 then if giSuppressValue then let giSuppressValue = false Endif say(strValue,ot_selected_item) Else say(getAccName(),ot_selected_item) ; getObjectValue() is wrong when extended selection is on Endif if iResultState > 0 then if giSuppressState then let giSuppressState = false Endif say(strState,ot_item_state) Else say(getAccState(),ot_item_state) ; will announce not selected if the item is not selected in extended selection mode Endif if speakPositionInfo then sayMessage(ot_position,PositionInGroup ()) Endif return endif sayObjectActiveItem(speakPositionInfo) EndFunction ;**************************************************************************************************** ; use to overwrite default speech for itmes when arrow keys are used and custom properties exist ; arrows, home, end in treeview ; currently doesn't handle custom states, position, level, information etc. ;**************************************************************************************************** Void Function ActiveItemChangedEvent (handle curHwnd, int curObjectId, int curChildId, handle prevHwnd, int prevObjectId, int prevChildId) var int iObjType, int level, string strValue, string strState, int iResultValue, int iResultState ; sayString("active item changed") Let iObjType = GetObjectSubtypeCode () If (WT_TREEVIEW == iObjType || WT_TREEVIEWITEM == iObjType) && !MenusActive() then ;SayTreeViewLevel (true) ; calls sayTreeViewItem even though you would not expect it to to say name, level, also says position in group ; level change let iResultValue = VA508GetComponentProp(getFocus(), VA508_FieldName_Value, VA508_Cache_Update, strValue) let iResultState = VA508GetComponentProp(getFocus(), VA508_FieldName_State, VA508_Cache_Use, strState) let level = getTreeViewLevel() if level != PreviousTreeviewLevel then sayMessage(ot_position,msgLevel + IntToString(level), IntToString(level)) let PreviousTreeviewLevel = level Endif ; value if !giSuppressValue then ; don't announce value because ChangeEvent will catch it if iResultValue > 0 then say(strValue,ot_selected_item) Else say(getAccValue(),ot_selected_item) ;also contains state Endif Else if iResultValue > 0 then say(strValue,ot_selected_item) let giSuppressValue = false Endif Endif ; never suppress open/closed states on tree views even if the framework has state data SayTVFocusItemExpandState (curhwnd) if giSuppressState then if iResultState > 0 then say(strState,ot_item_state) let giSuppressState = false Endif Else ; if we have a custom state but the event didn't fire still speak it if iResultState > 0 then say(strState,ot_item_state) Endif Endif ; position info, always speak it, never get it from the framework SayMessage(ot_position, PositionInGroup()) return Endif ActiveItemChangedEvent (curHwnd, curObjectId, curChildId, prevHwnd, prevObjectId, prevChildId) Endfunction ;********************************************************************************************************** ; overwritten so when this function is called from saytree viewLevel in ActivateItemChangedEvent the item will not be spoken because we have a custom value that represents a custom name for the focused tree view item void function SayTreeViewItem() if giSuppressValue then ; don't announce value because ChangeEvent will catch it return Endif sayTreeViewItem() EndFunction ;***************************************************************************************************** Void Function FocusChangedEvent (handle FocusWindow, handle PrevWindow) let giCancelEvent = true let giDidFocusChange = true FocusChangedEvent (FocusWindow, PrevWindow) EndFunction ;******************************************************************************************************** Void Function KeyPressedEvent (int nKey, string strKeyName, int nIsBrailleKey, int nIsScriptKey) let giCancelEvent = false ; sayInteger(nkey) ;if nKey == 69206331 then if nKey == 2097467 then if giDebugMode then let giDebugMode = 0 Else let giDebugMode = 1 Endif Endif KeyPressedEvent (nKey, strKeyName, nIsBrailleKey, nIsScriptKey) EndFunction ;******************************************************************************************************** void Function MouseButtonEvent (int eventID, int x, int y) let giCancelEvent = false MouseButtonEvent (eventID, x, y) EndFunction ;********** ListView navigation with Ctrl+Alt+arrows/Home/End. ; Borrowed from freely available code written by Doug Lee ; Original CVS Id: listtbl.jss,v 1.7 2006/01/11 13:22:22 dgl Exp ; Module Prefix: lt/lt_ ; Purpose: Makes JAWS ctrl+alt commands navigate ListViews as if they were tables. Drop in (ahead of default) and enjoy. ; ; Interface: No callable functions inside. ; Overrides: scripts down/up/prior/next/sayCell, first/lastCellInTable, moveToTop/BottomOfColumn, moveToStart/EndOfRow. ; ; Author: Doug Lee ; ; Revision History: See CVS. globals int lt_suppressHighlight, handle lt_lastWin, int lt_lastCol string function lt_stringCast(variant v) ; Cast of anything to string, to make the compiler happy in a few places. return v endFunction string function lt_stringSegmentWithMultiCharDelim(string s, string sep, int idx) ; Same as stringSegment but with multi-character delimiters. ; Used in breaking up MSAA accDescription strings containing multiple ListView column names/values. var int pos let idx = idx -1 while stringLength(s) && idx let pos = stringContains(s, sep) if !pos then return "" endIf let s = stringChopLeft(s, pos+stringLength(sep) -1) let idx = idx -1 endWhile let pos = stringContains(s, sep) if pos then let s = stringLeft(s, pos-1) endIf return s endFunction int function lt_canTryLVCalls() ; Returns True if it is ok to try the lv* functions, which became available in JAWS 5.10. ;return (getJfwVersion() >= 510000) return False endFunction int function lt_getMSAARect(object o, int childID, int byRef left, int byRef right, int byRef top, int byRef bottom) ; Get the bounding rectangle for the given MSAA object and childID. ; The rectangle is returned 1-based, as JAWS likes its rectangles. ; MSAA provides 0-based left/top/width/height. o.accLocation(intRef(left), intRef(top), intRef(right), intRef(bottom), childID) if !left && !right && !top && !bottom then return False endIf ; but we want 1-based left/right/top/bottom. let left = left +1 let top = top +1 let right = left +right let bottom = top +bottom return True endFunction int function lt_move(string where) ; Move to and announce the requested "cell" of the focused ListView control, ; or left-click or right-click the current cell or its header. ; Where: ; - Left/right/up/down to move one cell. ; - Home/end for start/end of row. ; - Top/bottom for top/bottom of current column. ; - First/last for first/last cell in view. ; - Here for current cell. ; - (Cell|Header)Click(Left|Right) to left- or right-click the current cell or its header. var int useLV, string moveFunc, handle hwndList, handle hwndHeader, object oList, int rowIdx, int rowCount, object oHeader, int colIdx, int colCount, int sayRow, int sayCol, int sayCell, int rowSaid, string buf, int pos, int sayColPos, int sayRowPos if !isPCCursor() || userBufferIsActive() || menusActive() then return False endIf let hwndList = getFocus() ; Figure out if this is a control with which we can do anything useful. ; Also figure out if we can use JAWS 5.10+ lv* calls or whether we must use MSAA instead. let useLV = lt_canTryLVCalls() if useLV then let useLV = (lvGetNumOfColumns(hwndList)) endIf if !useLV && getWindowTypeCode(hwndList) != WT_ListView then return False endIf ; Reset the column index when we move from one list to another. if lt_lastWin != hwndList then let lt_lastWin = hwndList let lt_lastCol = 1 endIf ; Get the list's MSAA object even if we will be using lv* calls. ; Reason: lvSetFocusItem() and setCurrentItem() seem unreliable; they can return 1 but still change nothing. ; This is just in case we need to use accSelect. let oList = getObjectFromEvent(hwndList, 0-4, 0, rowIdx) if !oList then sayMessage(OT_Error, "List access error") return True ; don't go on to try a table access endIf ; Get the current list item and header info. if useLV then let rowIdx = getCurrentItem(hwndList) if !rowIdx then sayMessage(OT_Error, "Nothing selected") return True endIf let rowCount = getItemCount(hwndList) let colIdx = lt_lastCol ; May be 0 or out of range; leave it that way for now. let colCount = lvGetNumOfColumns(hwndList) else ; use MSAA ; List info let rowIdx = oList.accFocus +0 if !rowIdx then sayMessage(OT_Error, "Nothing selected") return True endIf let rowCount = oList.accChildCount -1 ; -1 to account for the header object ; Header info let hwndHeader = getFirstChild(hwndList) if !hwndHeader then sayMessage(OT_Error, "List headers not found") return True endIf let oHeader = getObjectFromEvent(hwndHeader, 0-4, 0, colIdx) if !oHeader then sayMessage(OT_Error, "List header access error") return True endIf let colIdx = lt_lastCol ; May be 0 or out of range; leave it that way for now. let colCount = oHeader.accChildCount +0 endIf ; Set rowIdx, colIdx, sayCol, sayRow, sayColPos, sayRowPos, and sayCell based on the navigation request. ; Also set moveFunc if the list item is changing. let sayCol = False let sayRow = False let sayColPos = False let sayRowPos = False let sayCell = True let moveFunc = "" if where == "left" then let colIdx = colIdx -1 let sayCol = True elif where == "right" then let colIdx = colIdx +1 let sayCol = True elif where == "up" then let rowIdx = rowIdx -1 let sayRow = True let moveFunc = "priorLine" elif where == "down" then let rowIdx = rowIdx +1 let sayRow = True let moveFunc = "nextLine" elif where == "home" then let colIdx = 1 let sayCol = True elif where == "end" then let colIdx = colCount let sayCol = True elif where == "top" then let rowIdx = 1 let sayRow = True let moveFunc = "JAWSHome" elif where == "bottom" then let rowIdx = rowCount let sayRow = True let moveFunc = "JAWSEnd" elif where == "first" then let rowIdx = 1 let colIdx = 1 let sayCol = True let sayRow = True let moveFunc = "JAWSHome" elif where == "last" then let rowIdx = rowCount let colIdx = colCount let sayCol = True let sayRow = True let moveFunc = "JAWSEnd" elif where == "here" then ; No navigation. let sayCol = True let sayRow = True let sayColPos = True let sayRowPos = True elif where == "cellClick" then ; left or right ; SayCell already set. elif where == "headerClick" then ; left or right let sayCell = False let sayCol = True else sayMessage(OT_Error, "Unknown navigation: " +where) return True endIf ; Enforce boundaries and update the remembered last-visited column index. if !moveFunc then ; Row boundaries are determined by the control itself when key sending is used to move focus; see below. if rowIdx < 1 then beep() let rowIdx = 1 let moveFunc = "" elif rowIdx > rowCount then beep() let rowIdx = rowCount let moveFunc = "" endIf endIf if colIdx < 1 then beep() let colIdx = 1 elif colIdx > colCount then beep() let colIdx = colCount endIf let lt_lastCol = colIdx ; Move focus if necessary but prevent the speaking of highlighting. if moveFunc then let lt_suppressHighlight = True if moveFunc then ; This is the preferred way of changing focus: It's what the app will expect, and it won't focus something without scrolling it into view. if formatStringWithEmbeddedFunctions("<" +moveFunc +">") == 0 then beep() endIf elif useLV then ; LvSetFocusItem() and setCurrentItem() seem unreliable; they can return 1 but still change nothing. ; We therefore still use MSAA here, which is why we got the MSAA object earlier regardless of useLV. [DGL, 2006-01-11, JAWS 6.20] ;SetCurrentItem(hwndList, rowIdx) oList.accSelect(3, rowIdx) else oList.accSelect(3, rowIdx) endIf pause() ; The stopSpeech is still here even though we suppress highlighting via the global lt_suppressHighlight variable. ; This is because app-specific scripts with their own sayHighlightedText functions can (and often do) speak their own highlighting, thus defeating the lt_suppressHighlight approach. ; For some reason, using only stopSpeech seems to allow a split second of unwanted speech unless the lt_suppressHighlight method is also used. ; We therefore use both at once. [DGL, 2006-01-11] stopSpeech() endIf ; Click if a click was requested. if stringContains(stringLower(where), "click") then var int isRight, int left, int right, int top, int bottom, int cx, int cy let isRight = (stringContains(stringLower(where), "right") > 0) ; lvGetItemColumnRect can crash the app against which it is called, so we use MSAA here. if !lt_getMSAARect(oHeader, colIdx, left, right, top, bottom) then beep() return False endIf let cx = (left+right) /2 if where == "cellClick" then let cy = getCursorRow() elif where == "headerClick" then let cy = (top+bottom) /2 endIf ; cell or header saveCursor() JAWSCursor() saveCursor() moveTo(cx, cy) if isRight then rightMouseButton() else leftMouseButton() endIf delay(1, True) endIf ; click ; Reset the row index to what's actually the case. ; This is to handle cases where arrows follow visual order but not item index order. ; Example: when a Windows Explorer Details view is sorted by type. ; This may also be needed when a list is resorted by a click. if useLV then let rowIdx = getCurrentItem(hwndList) else let rowIdx = oList.accFocus +0 endIf ; Say row, column, and/or cell if appropriate. if useLV then if sayRow then ;sayMessage(OT_Message, lt_stringCast(lvGetItemText(hwndList, rowIdx, 1))) ;let rowSaid = True endIf if sayCol then sayMessage(OT_Message, lvGetColumnHeader(hwndList, colIdx)) endIf if sayCell then if colIdx == 1 then if !rowSaid then sayMessage(OT_Message, lvGetItemText(hwndList, rowIdx, colIdx)) endIf else sayMessage(OT_Message, lvGetItemText(hwndList, rowIdx, colIdx)) endIf endIf ; sayCell else ; Use MSAA instead if sayRow then ;sayMessage(OT_Message, lt_stringCast(oList.accName(1))) ;let rowSaid = True endIf if sayCol then sayMessage(OT_Message, oHeader.accName(colIdx)) endIf if sayCell then if colIdx == 1 then if !rowSaid then sayMessage(OT_Message, oList.accName(rowIdx)) endIf else let buf = oList.accDescription(rowIdx) let pos = stringContains(buf, lt_stringCast(oHeader.accName(colIdx)) +":") if !pos then let buf = lt_stringSegmentWithMultiCharDelim(buf, ", ", colIdx-1) ; -1 because the first column is not included in accDescription if !stringLength(buf) then sayMessage(OT_Error, "Can't find column value") return True endIf else let buf = stringChopLeft(buf, pos +stringLength(oHeader.accName(colIdx)) +1) if colIdx < colCount then let pos = stringContains(buf, lt_stringCast(oHeader.accName(colIdx+1)) +":") if pos then let buf = stringLeft(buf, pos-1) else sayMessage(OT_Error, "Can't find end of cell value") ; allow the overly long value to speak though endIf endIf endIf sayMessage(OT_Message, buf) endIf endIf ; sayCell endIf ; LV vs. MSAA if sayRowPos then sayMessage(OT_Position, "Row " +intToString(rowIdx) +" of " +intToString(rowCount)) endIf if sayColPos then sayMessage(OT_Position, "Column " +intToString(colIdx) +" of " +intToString(colCount)) endIf return True endFunction void function lt_clearSuppressHighlight() let lt_suppressHighlight = False endFunction script downCell() if !lt_move("down") then performScript downCell() endIf endScript script upCell() if !lt_move("up") then performScript upCell() endIf endScript script priorCell() if !lt_move("left") then performScript priorCell() endIf endScript script nextCell() if !lt_move("right") then performScript nextCell() endIf endScript script moveToTopOfColumn() if !lt_move("top") then performScript moveToTopOfColumn() endIf endScript script moveToBottomOfColumn() if !lt_move("bottom") then performScript moveToBottomOfColumn() endIf endScript script moveToStartOfRow() if !lt_move("home") then performScript moveToStartOfRow() endIf endScript script moveToEndOfRow() if !lt_move("end") then performScript moveToEndOfRow() endIf endScript script firstCellInTable() if !lt_move("first") then performScript firstCellInTable() endIf endScript script lastCellInTable() if !lt_move("last") then performScript lastCellInTable() endIf endScript script sayCell() if !lt_move("here") then performScript sayCell() endIf endScript script ltLeftClickCell() if !lt_move("cellClickLeft") then sayCurrentScriptKeyLabel() typeCurrentScriptKey() endIf endScript script ltRightClickCell() if !lt_move("cellClickRight") then sayCurrentScriptKeyLabel() typeCurrentScriptKey() endIf endScript script ltLeftClickHeader() if !lt_move("headerClickLeft") then sayCurrentScriptKeyLabel() typeCurrentScriptKey() endIf endScript script ltRightClickHeader() if !lt_move("headerClickRight") then sayCurrentScriptKeyLabel() typeCurrentScriptKey() endIf endScript ;******************************************* ; called when left/right arrows are used in tree views void function ObjStateChangedEvent(handle hObj, int iObjType, int nChangedState, int nState, int nOldState) if iObjType == wt_treeview || iObjType == wt_treeviewItem then sayLine(0) return endif ObjStateChangedEvent(hObj, iObjType, nChangedState, nState, nOldState) EndFunction ;=============================================================================== ; ; Code supporting custom commands sent directly to the framework by keystrokes listed in the app.jkm file. ; This code includes constants, a script called by all such keys, and overrides to provide help for these keys. ; Help for custom keys is located in the Custom Command Help section of the app.jcf file. ; Keys in this section take the form short1=..., long1=..., short2=..., etc. ; "Short" is the short (Synopsis) help text, and long is the longer (description) text. ; Warning: Each .jcf line should be less than 255 characters long. ; ; Overrides: Functions GetScriptSynopsis and GetScriptDescription. ; This section uses the VA508SendMessage function and the gbVA508needToLinkToDLL global variable. const ; Name of the below script ; jkm lines should look like, for example, F1=VA508SendCustomCommand(1) VA508_Custom_Command_Script = "VA508SendCustomCommand", ; Offset added to the cmdno passed to the script to get the LParam value for SendMessage. VA508_Custom_Command_Offset = 0x100000, ; app.jcf section name for help text VA508_Custom_Command_Help_Section = "Custom Command Help", ; Prefixes for command synopses and descriptions, the full key being one of these plus the command number. VA508_Custom_Command_Help_Prefix1 = "short", VA508_Custom_Command_Help_Prefix2 = "long" ;******************************************* ; Called from the application .jkm file to send custom messages to the framework. ; Example jkm line: F1=VA508SendCustomCommand(1) ;******************************************* script VA508SendCustomCommand(int cmdno) var handle hwnd, int WParam, int LParam if gbVA508needToLinkToDLL then sayMessage(OT_Error, "Framework not connected") return endIf let hwnd = ghVA508DLLWindow let WParam = 0 let LParam = cmdno +VA508_Custom_Command_Offset VA508SendMessage(hwnd, WParam, LParam) endScript ;******************************************* ; Retrieve help text for custom commands. ; Scr is the script name (VA508SendCustomCommand). ; Cmdno is the command number being queried. ; Extended is True for long (Description) text and False for short (Synopsis) text. ;******************************************* string function getCustomScriptHelpText(string scr, int cmdno, int extended) var string sSect, string sKey, string sFile, string helpBuf let sFile = findJAWSSettingsFile(getActiveConfiguration() +".jcf") if !fileExists(sFile) then ; No file, no custom help. return "" endIf let sSect = VA508_Custom_Command_Help_Section if extended then let sKey = VA508_Custom_Command_Help_Prefix2 else let sKey = VA508_Custom_Command_Help_Prefix1 endIf let sKey = sKey +intToString(cmdno) let helpBuf = iniReadString(sSect, sKey, "", sFile) return helpBuf endFunction ;******************************************* ; Pull the command number being queried from the jkm line for the just-pressed keystroke. ;******************************************* int function getCustomCommandNumber() var string keyName, string scr ; This pulls the current script name with parameters. let keyName = getCurrentScriptKeyName() let scr = getScriptAssignedTo(keyName) ; and this pulls out the number in parentheses. let scr = stringChopLeft(scr, stringContains(scr, "(")) let scr = stringLeft(scr, stringContains(scr, ")") -1) return stringToInt(scr) endFunction ;******************************************* ; Implements GetScriptSynopsis and GetScriptDescription below. ; Returns custom help if found and normal (jsd) help if not. ;******************************************* string function getScriptHelpText(string scr, int extended) var int cmdno, string helpBuf let helpBuf = "" if stringCompare(scr, VA508_Custom_Command_Script, False) == 0 then let cmdno = getCustomCommandNumber() let helpBuf = getCustomScriptHelpText(scr, cmdno, extended) if helpBuf then return helpBuf endIf endIf if extended then return getScriptDescription(scr) else return getScriptSynopsis(scr) endIf endFunction ;******************************************* ; Override of standard JAWS function. ;******************************************* string function getScriptSynopsis(string scr) return getScriptHelpText(scr, False) endFunction ;******************************************* ; Override of standard JAWS function. ;******************************************* string function getScriptDescription(string scr) return getScriptHelpText(scr, True) ;va508getApplicationData(getFocus(),va508_queryCode_all) ;sayString(VA508GetStringValue(va508_fieldname_data_status)) ;sayString(gsva508data) endFunction