| [829] | 1 | unit JAWSImplementation; | 
|---|
|  | 2 |  | 
|---|
|  | 3 | interface | 
|---|
|  | 4 | { DONE -oJeremy Merrill -c508 : | 
|---|
|  | 5 | Add something that prevents overwriting of the script files if another | 
|---|
|  | 6 | app is running that's using the JAWS DLL } | 
|---|
|  | 7 | { TODO -oJeremy Merrill -c508 : Add check in here to look at script version in JSS file } | 
|---|
|  | 8 | { DONE -oJeremy Merrill -c508 : | 
|---|
|  | 9 | Replace registry communication with multiple windows - save strings in the window titles | 
|---|
|  | 10 | Use EnumerateChildWindows jaws script function in place of the FindWindow function | 
|---|
|  | 11 | that's being used right now.- EnumerateChildWindows with a window handle of 0 | 
|---|
|  | 12 | enumerates all windows on the desktop.  Will have to use the first part of the window | 
|---|
|  | 13 | title as an ID, and the last part as the string values.  Will need to check for a maximum | 
|---|
|  | 14 | string lenght, probably have to use multiple windows for long text. | 
|---|
|  | 15 | Will also beed to have a global window shared by muiltiple instances of the JAWS.SR DLL. } | 
|---|
|  | 16 | { DONE -oJeremy Merrill -c508 : | 
|---|
|  | 17 | Need to add version checking to TVA508AccessibilityManager component | 
|---|
|  | 18 | and JAWS.DLL.  Warning needs to display just like JAWS.DLL and JAWS. } | 
|---|
|  | 19 | uses SysUtils, Windows, Classes, Registry, StrUtils, Forms, Dialogs, | 
|---|
|  | 20 | ExtCtrls, VAUtils, DateUtils, PSApi, IniFiles, ActiveX, | 
|---|
|  | 21 | SHFolder, ShellAPI, VA508AccessibilityConst; | 
|---|
|  | 22 |  | 
|---|
|  | 23 | {$I 'VA508ScreenReaderDLLStandard.inc'} | 
|---|
|  | 24 |  | 
|---|
|  | 25 | { DONE -oJeremy Merrill -c508 :Figure out why Delphi IDE is loading the DLL when JAWS is running  - | 
|---|
|  | 26 | probably has something to do with the VA508 package being installed - | 
|---|
|  | 27 | need to test for csDesigning some place that we're not testing for (maybe?)} | 
|---|
|  | 28 |  | 
|---|
|  | 29 | exports Initialize, ShutDown, RegisterCustomBehavior, ComponentData, | 
|---|
|  | 30 | SpeakText, IsRunning, ConfigChangePending; | 
|---|
|  | 31 |  | 
|---|
|  | 32 | implementation | 
|---|
|  | 33 |  | 
|---|
|  | 34 | uses fVA508HiddenJawsMainWindow, FSAPILib_TLB, ComObj; | 
|---|
|  | 35 |  | 
|---|
|  | 36 | const | 
|---|
|  | 37 | //  JAWS_REQUIRED_VERSION     = '7.10.500'; in VA508AccessibilityConst unit | 
|---|
|  | 38 | JAWS_COM_OBJECT_VERSION = '8.0.2173'; | 
|---|
|  | 39 |  | 
|---|
|  | 40 | VA508_REG_PARAM_KEY = 'Software\Vista\508\JAWS'; | 
|---|
|  | 41 |  | 
|---|
|  | 42 | VA508_REG_COMPONENT_CAPTION = 'Caption'; | 
|---|
|  | 43 | VA508_REG_COMPONENT_VALUE = 'Value'; | 
|---|
|  | 44 | VA508_REG_COMPONENT_CONTROL_TYPE = 'ControlType'; | 
|---|
|  | 45 | VA508_REG_COMPONENT_STATE = 'State'; | 
|---|
|  | 46 | VA508_REG_COMPONENT_INSTRUCTIONS = 'Instructions'; | 
|---|
|  | 47 | VA508_REG_COMPONENT_ITEM_INSTRUCTIONS = 'ItemInstructions'; | 
|---|
|  | 48 | VA508_REG_COMPONENT_DATA_STATUS = 'DataStatus'; | 
|---|
|  | 49 |  | 
|---|
|  | 50 | VA508_ERRORS_SHOWN_STATE = 'ErrorsShown'; | 
|---|
|  | 51 |  | 
|---|
|  | 52 | RELOAD_CONFIG_SCRIPT = 'VA508Reload'; | 
|---|
|  | 53 |  | 
|---|
|  | 54 | SLASH = '\'; | 
|---|
|  | 55 | { TODO -oJeremy Merrill -c508 : | 
|---|
|  | 56 | Change APP_DATA so that "application data" isn't used - Windows Vista | 
|---|
|  | 57 | doesn't use this value - get data from Windows API call } | 
|---|
|  | 58 | APP_DATA = SLASH + 'application data' + SLASH; | 
|---|
|  | 59 | JAWS_COMMON_SCRIPT_PATH_TEXT = '\freedom scientific\jaws\'; | 
|---|
|  | 60 | JAWS_COMMON_SCRIPT_PATH_TEXT_LEN = length(JAWS_COMMON_SCRIPT_PATH_TEXT); | 
|---|
|  | 61 |  | 
|---|
|  | 62 | type | 
|---|
|  | 63 | TCompareType = (jcPrior, jcINI, jcLineItems, jcVersion, jcScriptMerge); | 
|---|
|  | 64 |  | 
|---|
|  | 65 | TFileInfo = record | 
|---|
|  | 66 | AppFile: boolean; | 
|---|
|  | 67 | Ext: string; | 
|---|
|  | 68 | CompareType: TCompareType; | 
|---|
|  | 69 | Required: boolean; | 
|---|
|  | 70 | Compile: boolean; | 
|---|
|  | 71 | end; | 
|---|
|  | 72 |  | 
|---|
|  | 73 | const | 
|---|
|  | 74 | JAWS_SCRIPT_NAME = 'VA508JAWS'; | 
|---|
|  | 75 |  | 
|---|
|  | 76 | JAWS_SCRIPT_VERSION = 'VA508_Script_Version'; | 
|---|
|  | 77 | CompiledScriptFileExtension = '.JSB'; | 
|---|
|  | 78 | ScriptFileExtension = '.JSS'; | 
|---|
|  | 79 | ScriptDocExtension = '.JSD'; | 
|---|
|  | 80 | ConfigFileExtension = '.JCF'; | 
|---|
|  | 81 | KeyMapExtension = '.JKM'; | 
|---|
|  | 82 | DictionaryFileExtension = '.JDF'; | 
|---|
|  | 83 |  | 
|---|
|  | 84 | FileInfo: array[1..6] of TFileInfo = ( | 
|---|
|  | 85 | (AppFile: FALSE; Ext: ScriptFileExtension;     CompareType: jcVersion;     Required: TRUE;  Compile: TRUE), | 
|---|
|  | 86 | (AppFile: FALSE; Ext: ScriptDocExtension;      CompareType: jcPrior;       Required: TRUE;  Compile: FALSE), | 
|---|
|  | 87 | (AppFile: TRUE;  Ext: ScriptFileExtension;     CompareType: jcScriptMerge; Required: TRUE;  Compile: TRUE), | 
|---|
|  | 88 | (AppFile: TRUE;  Ext: ConfigFileExtension;     CompareType: jcINI;         Required: TRUE;  Compile: FALSE), | 
|---|
|  | 89 | (AppFile: TRUE;  Ext: DictionaryFileExtension; CompareType: jcLineItems;   Required: FALSE; Compile: FALSE), | 
|---|
|  | 90 | (AppFile: TRUE;  Ext: KeyMapExtension;         CompareType: jcINI;         Required: FALSE; Compile: FALSE)); | 
|---|
|  | 91 |  | 
|---|
|  | 92 | JAWS_VERSION_ERROR = ERROR_INTRO + | 
|---|
|  | 93 | 'The Accessibility Framework can only communicate with JAWS ' + JAWS_REQUIRED_VERSION + CRLF + | 
|---|
|  | 94 | 'or later versions.  Please update your version of JAWS to a minimum of' + CRLF + | 
|---|
|  | 95 | JAWS_REQUIRED_VERSION + ', or preferably the most recent release, to allow the Accessibility' + CRLF + | 
|---|
|  | 96 | 'Framework to communicate with JAWS.  If you are getting this message' + CRLF + | 
|---|
|  | 97 | 'and you already have a compatible version of JAWS, please contact your' + CRLF + | 
|---|
|  | 98 | 'system administrator, and request that they run, with administrator rights,' + CRLF + | 
|---|
|  | 99 | 'the JAWSUpdate application located in the \Program Files\VistA\' + CRLF + | 
|---|
|  | 100 | 'Common Files directory. JAWSUpdate is not required for JAWS' + CRLF + | 
|---|
|  | 101 | 'versions ' + JAWS_COM_OBJECT_VERSION + ' and above.' + CRLF; | 
|---|
|  | 102 |  | 
|---|
|  | 103 | JAWS_FILE_ERROR = ERROR_INTRO + | 
|---|
|  | 104 | 'The JAWS interface with the Accessibility Framework requires the ability' + CRLF + | 
|---|
|  | 105 | 'to write files to the hard disk, but the following error is occurring trying to' + CRLF + | 
|---|
|  | 106 | 'write to the disk:' + CRLF + '%s' + CRLF + | 
|---|
|  | 107 | 'Please contact your system administrator in order to ensure that your ' + CRLF + | 
|---|
|  | 108 | 'security privileges allow you to write files to the hard disk.' + CRLF + | 
|---|
|  | 109 | 'If you are sure you have these privileges, your hard disk may be full.  Until' + CRLF + | 
|---|
|  | 110 | 'this problem is resolved, the Accessibility Framework will not be able to' + CRLF + | 
|---|
|  | 111 | 'communicate with JAWS.'; | 
|---|
|  | 112 |  | 
|---|
|  | 113 | JAWS_USER_MISSMATCH_ERROR = ERROR_INTRO + | 
|---|
|  | 114 | 'An error has been detected in the state of JAWS that will not allow the' + CRLF + | 
|---|
|  | 115 | 'Accessibility Framework to communicate with JAWS until JAWS is shut' + CRLF + | 
|---|
|  | 116 | 'down and restarted.  Please restart JAWS at this time.'; | 
|---|
|  | 117 |  | 
|---|
|  | 118 | DLL_VERSION_ERROR = ERROR_INTRO + | 
|---|
|  | 119 | 'The Accessibility Framework is at version %s, but the required JAWS' + CRLF + | 
|---|
|  | 120 | 'support files are only at version %s.  The new support files should have' + CRLF + | 
|---|
|  | 121 | 'been released with the latest version of the software you are currently' + CRLF + | 
|---|
|  | 122 | 'running.  The Accessibility Framework will not be able to communicate' + CRLF + | 
|---|
|  | 123 | 'with JAWS until these support files are installed.  Please contact your' + CRLF + | 
|---|
|  | 124 | 'system administrator for assistance.'; | 
|---|
|  | 125 |  | 
|---|
|  | 126 | JAWS_ERROR_VERSION = 1; | 
|---|
|  | 127 | JAWS_ERROR_FILE_IO = 2; | 
|---|
|  | 128 | JAWS_ERROR_USER_PROBLEM = 3; | 
|---|
|  | 129 | DLL_ERROR_VERSION = 4; | 
|---|
|  | 130 |  | 
|---|
|  | 131 | JAWS_ERROR_COUNT = 4; | 
|---|
|  | 132 |  | 
|---|
|  | 133 | JAWS_RELOAD_DELAY = 500; | 
|---|
|  | 134 |  | 
|---|
|  | 135 | var | 
|---|
|  | 136 | JAWSErrorMessage: array[1..JAWS_ERROR_COUNT] of string = (JAWS_VERSION_ERROR, JAWS_FILE_ERROR, | 
|---|
|  | 137 | JAWS_USER_MISSMATCH_ERROR, DLL_VERSION_ERROR); | 
|---|
|  | 138 |  | 
|---|
|  | 139 | JAWSErrorsShown: array[1..JAWS_ERROR_COUNT] of boolean  = (FALSE, FALSE, FALSE, FALSE); | 
|---|
|  | 140 |  | 
|---|
|  | 141 | type | 
|---|
|  | 142 | TJAWSSayString = function(StringToSpeak: PChar; Interrupt: BOOL): BOOL; stdcall; | 
|---|
|  | 143 | TJAWSRunScript = function(ScriptName: PChar): BOOL; stdcall; | 
|---|
|  | 144 |  | 
|---|
|  | 145 | TStartupID = record | 
|---|
|  | 146 | Handle: HWND; | 
|---|
|  | 147 | InstanceID: Integer; | 
|---|
|  | 148 | MsgID: Integer; | 
|---|
|  | 149 | end; | 
|---|
|  | 150 |  | 
|---|
|  | 151 | TJAWSManager = class | 
|---|
|  | 152 | strict private | 
|---|
|  | 153 | FRequiredFilesFound: boolean; | 
|---|
|  | 154 | FMainForm: TfrmVA508HiddenJawsMainWindow; | 
|---|
|  | 155 | FWasShutdown: boolean; | 
|---|
|  | 156 | FJAWSFileError: string; | 
|---|
|  | 157 | FDictionaryFileName: string; | 
|---|
|  | 158 | FConfigFile: string; | 
|---|
|  | 159 | FKeyMapFile: string; | 
|---|
|  | 160 | FMasterApp: string; | 
|---|
|  | 161 | FRootScriptFileName: string; | 
|---|
|  | 162 | FRootScriptAppFileName: string; | 
|---|
|  | 163 | FDefaultScriptDir: string; | 
|---|
|  | 164 | FUserStriptDir: string; | 
|---|
|  | 165 | FKeyMapINIFile: TINIFile; | 
|---|
|  | 166 | FKeyMapINIFileModified: boolean; | 
|---|
|  | 167 | FAssignedKeys: TStringList; | 
|---|
|  | 168 | FConfigINIFile: TINIFile; | 
|---|
|  | 169 | FConfigINIFileModified: boolean; | 
|---|
|  | 170 | FDictionaryFile: TStringList; | 
|---|
|  | 171 | FDictionaryFileModified: boolean; | 
|---|
|  | 172 | FCompiler: string; | 
|---|
|  | 173 | JAWSAPI: IJawsApi; | 
|---|
|  | 174 | private | 
|---|
|  | 175 | procedure ShutDown; | 
|---|
|  | 176 | procedure MakeFileWritable(FileName: string); | 
|---|
|  | 177 | procedure LaunchMasterApplication; | 
|---|
|  | 178 | procedure KillINIFiles(Sender: TObject); | 
|---|
|  | 179 | procedure ReloadConfiguration; | 
|---|
|  | 180 | public | 
|---|
|  | 181 | constructor Create; | 
|---|
|  | 182 | destructor Destroy; override; | 
|---|
|  | 183 | class procedure ShowError(ErrorNumber: integer); overload; | 
|---|
|  | 184 | class procedure ShowError(ErrorNumber: integer; data: array of const); overload; | 
|---|
|  | 185 | class function GetPathFromJAWS(PathID: integer; DoLowerCase: boolean = TRUE): string; | 
|---|
|  | 186 | class function GetJAWSWindow: HWnd; | 
|---|
|  | 187 | class function IsRunning(HighVersion, LowVersion: Word): BOOL; | 
|---|
|  | 188 | function Initialize(ComponentCallBackProc: TComponentDataRequestProc): BOOL; | 
|---|
|  | 189 | procedure SendComponentData(WindowHandle: HWND; DataStatus: LongInt; Caption, Value, Data, | 
|---|
|  | 190 | ControlType, State, Instructions, ItemInstructions: PChar); | 
|---|
|  | 191 | procedure SpeakText(Text: PChar); | 
|---|
|  | 192 | procedure RegisterCustomBehavior(Before, After: string; Action: integer); | 
|---|
|  | 193 | class function JAWSVersionOK: boolean; | 
|---|
|  | 194 | class function JAWSTalking2CurrentUser: boolean; | 
|---|
|  | 195 | function FileErrorExists: boolean; | 
|---|
|  | 196 | property RequiredFilesFound: boolean read FRequiredFilesFound; | 
|---|
|  | 197 | property MainForm: TfrmVA508HiddenJawsMainWindow read FMainForm; | 
|---|
|  | 198 | end; | 
|---|
|  | 199 |  | 
|---|
|  | 200 | var | 
|---|
|  | 201 | JAWSManager: TJAWSManager = nil; | 
|---|
|  | 202 | DLLMessageID: UINT = 0; | 
|---|
|  | 203 |  | 
|---|
|  | 204 | procedure EnsureManager; | 
|---|
|  | 205 | begin | 
|---|
|  | 206 | if not assigned(JAWSManager) then | 
|---|
|  | 207 | JAWSManager := TJAWSManager.Create; | 
|---|
|  | 208 | end; | 
|---|
|  | 209 |  | 
|---|
|  | 210 | // Checks to see if the screen reader is currently running | 
|---|
|  | 211 | function IsRunning(HighVersion, LowVersion: Word): BOOL; stdcall; | 
|---|
|  | 212 | begin | 
|---|
|  | 213 | Result := TJAWSManager.IsRunning(HighVersion, LowVersion); | 
|---|
|  | 214 | end; | 
|---|
|  | 215 |  | 
|---|
|  | 216 | // Executed after IsRunning returns TRUE, when the DLL is accepted as the screen reader of choice | 
|---|
|  | 217 | function Initialize(ComponentCallBackProc: TComponentDataRequestProc): BOOL; stdcall; | 
|---|
|  | 218 | begin | 
|---|
|  | 219 | EnsureManager; | 
|---|
|  | 220 | Result := JAWSManager.Initialize(ComponentCallBackProc); | 
|---|
|  | 221 | end; | 
|---|
|  | 222 |  | 
|---|
|  | 223 | // Executed when the DLL is unloaded or screen reader is no longer needed | 
|---|
|  | 224 | procedure ShutDown; stdcall; | 
|---|
|  | 225 | begin | 
|---|
|  | 226 | if assigned(JAWSManager) then | 
|---|
|  | 227 | begin | 
|---|
|  | 228 | JAWSManager.ShutDown; | 
|---|
|  | 229 | FreeAndNil(JAWSManager); | 
|---|
|  | 230 | end; | 
|---|
|  | 231 | end; | 
|---|
|  | 232 |  | 
|---|
|  | 233 | function ConfigChangePending: boolean; stdcall; | 
|---|
|  | 234 | begin | 
|---|
|  | 235 | Result := FALSE; | 
|---|
|  | 236 | if assigned(JAWSManager) and assigned(JAWSManager.MainForm) and | 
|---|
|  | 237 | (JAWSManager.MainForm.ConfigChangePending) then | 
|---|
|  | 238 | Result := TRUE; | 
|---|
|  | 239 | end; | 
|---|
|  | 240 |  | 
|---|
|  | 241 | // Returns Component Data as requested by the screen reader | 
|---|
|  | 242 | procedure ComponentData(WindowHandle: HWND; | 
|---|
|  | 243 | DataStatus:   LongInt = DATA_NONE; | 
|---|
|  | 244 | Caption:      PChar = nil; | 
|---|
|  | 245 | Value:        PChar = nil; | 
|---|
|  | 246 | Data:         PChar = nil; | 
|---|
|  | 247 | ControlType:  PChar = nil; | 
|---|
|  | 248 | State:        PChar = nil; | 
|---|
|  | 249 | Instructions: PChar = nil; | 
|---|
|  | 250 | ItemInstructions: PChar = nil); stdcall; | 
|---|
|  | 251 | begin | 
|---|
|  | 252 | EnsureManager; | 
|---|
|  | 253 | JAWSManager.SendComponentData(WindowHandle, DataStatus, Caption, Value, Data, ControlType, State, | 
|---|
|  | 254 | Instructions, ItemInstructions); | 
|---|
|  | 255 | end; | 
|---|
|  | 256 |  | 
|---|
|  | 257 | // Instructs the Screen Reader to say the specified text | 
|---|
|  | 258 | procedure SpeakText(Text: PChar); stdcall; | 
|---|
|  | 259 | begin | 
|---|
|  | 260 | EnsureManager; | 
|---|
|  | 261 | JAWSManager.SpeakText(Text); | 
|---|
|  | 262 | end; | 
|---|
|  | 263 |  | 
|---|
|  | 264 | procedure RegisterCustomBehavior(BehaviorType: integer; Before, After: PChar); | 
|---|
|  | 265 | begin | 
|---|
|  | 266 | EnsureManager; | 
|---|
|  | 267 | JAWSManager.RegisterCustomBehavior(Before, After, BehaviorType); | 
|---|
|  | 268 | end; | 
|---|
|  | 269 |  | 
|---|
|  | 270 | { TJAWSManager } | 
|---|
|  | 271 |  | 
|---|
|  | 272 | const | 
|---|
|  | 273 | {$WARNINGS OFF} // Don't care about platform specific warning | 
|---|
|  | 274 | NON_WRITABLE_FILE_ATTRIB = faReadOnly or faHidden; | 
|---|
|  | 275 | {$WARNINGS ON} | 
|---|
|  | 276 | WRITABLE_FILE_ATTRIB = faAnyFile and (not NON_WRITABLE_FILE_ATTRIB); | 
|---|
|  | 277 |  | 
|---|
|  | 278 | procedure TJAWSManager.MakeFileWritable(FileName: string); | 
|---|
|  | 279 | var | 
|---|
|  | 280 | Attrib: integer; | 
|---|
|  | 281 | begin | 
|---|
|  | 282 | {$WARNINGS OFF} // Don't care about platform specific warning | 
|---|
|  | 283 | Attrib := FileGetAttr(FileName); | 
|---|
|  | 284 | {$WARNINGS ON} | 
|---|
|  | 285 | if (Attrib and NON_WRITABLE_FILE_ATTRIB) <> 0 then | 
|---|
|  | 286 | begin | 
|---|
|  | 287 | Attrib := Attrib and WRITABLE_FILE_ATTRIB; | 
|---|
|  | 288 | {$WARNINGS OFF} // Don't care about platform specific warning | 
|---|
|  | 289 | if FileSetAttr(FileName, Attrib) <> 0 then | 
|---|
|  | 290 | {$WARNINGS ON} | 
|---|
|  | 291 | FJAWSFileError := 'Could not change read-only attribute of file "' + FileName + '"'; | 
|---|
|  | 292 | end; | 
|---|
|  | 293 | end; | 
|---|
|  | 294 |  | 
|---|
|  | 295 | var | 
|---|
|  | 296 | JAWSMsgID: UINT = 0; | 
|---|
|  | 297 |  | 
|---|
|  | 298 | const | 
|---|
|  | 299 | JAWS_MESSAGE_ID = 'JW_GET_FILE_PATH'; | 
|---|
|  | 300 | // version is in directory after JAWS \Freedom Scientific\JAWS\*.*\... | 
|---|
|  | 301 | JAWS_PATH_ID_APPLICATION = 0; | 
|---|
|  | 302 | JAWS_PATH_ID_USER_SCRIPT_FILES = 1; | 
|---|
|  | 303 | JAWS_PATH_ID_JAWS_DEFAULT_SCRIPT_FILES = 2; | 
|---|
|  | 304 | // 0 = C:\Program Files\Freedom Scientific\JAWS\8.0\jfw.INI | 
|---|
|  | 305 | // 1 = D:\Documents and Settings\vhaislmerrij\Application Data\Freedom Scientific\JAWS\8.0\USER.INI | 
|---|
|  | 306 | // 2 = D:\Documents and Settings\All Users\Application Data\Freedom Scientific\JAWS\8.0\Settings\enu\DEFAULT.SBL | 
|---|
|  | 307 |  | 
|---|
|  | 308 | class function TJAWSManager.GetPathFromJAWS(PathID: integer; DoLowerCase: boolean = TRUE): string; | 
|---|
|  | 309 | var | 
|---|
|  | 310 | atm: ATOM; | 
|---|
|  | 311 | len: integer; | 
|---|
|  | 312 | path: string; | 
|---|
|  | 313 | JAWSWindow: HWnd; | 
|---|
|  | 314 | begin | 
|---|
|  | 315 | JAWSWindow := GetJAWSWindow; | 
|---|
|  | 316 | if JAWSMsgID = 0 then | 
|---|
|  | 317 | JAWSMsgID := RegisterWindowMessage(JAWS_MESSAGE_ID); | 
|---|
|  | 318 | Result := ''; | 
|---|
|  | 319 | atm := SendMessage(JAWSWindow, JAWSMsgID, PathID, 0); | 
|---|
|  | 320 | if atm <> 0 then | 
|---|
|  | 321 | begin | 
|---|
|  | 322 | SetLength(path, MAX_PATH * 2); | 
|---|
|  | 323 | len := GlobalGetAtomName(atm, PChar(path), MAX_PATH * 2); | 
|---|
|  | 324 | GlobalDeleteAtom(atm); | 
|---|
|  | 325 | if len > 0 then | 
|---|
|  | 326 | begin | 
|---|
|  | 327 | SetLength(path, len); | 
|---|
|  | 328 | Result := ExtractFilePath(path); | 
|---|
|  | 329 | if DoLowerCase then | 
|---|
|  | 330 | Result := LowerCase(Result); | 
|---|
|  | 331 | end; | 
|---|
|  | 332 | end; | 
|---|
|  | 333 | end; | 
|---|
|  | 334 |  | 
|---|
|  | 335 |  | 
|---|
|  | 336 | constructor TJAWSManager.Create; | 
|---|
|  | 337 | const | 
|---|
|  | 338 | COMPILER_FILENAME = 'scompile.exe'; | 
|---|
|  | 339 | JAWS_APP_NAME = 'VA508APP'; | 
|---|
|  | 340 | JAWSMasterApp = 'VA508JAWSDispatcher.exe'; | 
|---|
|  | 341 |  | 
|---|
|  | 342 | procedure FindCompiler; | 
|---|
|  | 343 | var | 
|---|
|  | 344 | compiler: string; | 
|---|
|  | 345 |  | 
|---|
|  | 346 | begin | 
|---|
|  | 347 | compiler := GetPathFromJAWS(JAWS_PATH_ID_APPLICATION); | 
|---|
|  | 348 | compiler := AppendBackSlash(compiler) + COMPILER_FILENAME; | 
|---|
|  | 349 | if FileExists(compiler) then | 
|---|
|  | 350 | FCompiler := compiler; | 
|---|
|  | 351 | end; | 
|---|
|  | 352 |  | 
|---|
|  | 353 | procedure FindJAWSRequiredFiles; | 
|---|
|  | 354 | var | 
|---|
|  | 355 | Path: string; | 
|---|
|  | 356 | i: integer; | 
|---|
|  | 357 | FileName: string; | 
|---|
|  | 358 | info: TFileInfo; | 
|---|
|  | 359 |  | 
|---|
|  | 360 | begin | 
|---|
|  | 361 | SetLength(Path, MAX_PATH); | 
|---|
|  | 362 | SetLength(Path, GetModuleFileName(HInstance, PChar(Path), Length(Path))); | 
|---|
|  | 363 | Path := ExtractFilePath(Path); | 
|---|
|  | 364 | Path := AppendBackSlash(Path); | 
|---|
|  | 365 | // look for the script files in the same directory as this DLL | 
|---|
|  | 366 | FRootScriptFileName := Path + JAWS_SCRIPT_NAME; | 
|---|
|  | 367 | FRootScriptAppFileName := Path + JAWS_APP_NAME; | 
|---|
|  | 368 | FRequiredFilesFound := TRUE; | 
|---|
|  | 369 | for i := low(FileInfo) to high(FileInfo) do | 
|---|
|  | 370 | begin | 
|---|
|  | 371 | info := FileInfo[i]; | 
|---|
|  | 372 | if info.Required then | 
|---|
|  | 373 | begin | 
|---|
|  | 374 | if info.AppFile then | 
|---|
|  | 375 | FileName := FRootScriptAppFileName + info.Ext | 
|---|
|  | 376 | else | 
|---|
|  | 377 | FileName := FRootScriptFileName + info.Ext; | 
|---|
|  | 378 | if not FileExists(FileName) then | 
|---|
|  | 379 | begin | 
|---|
|  | 380 | FRequiredFilesFound := FALSE; | 
|---|
|  | 381 | break; | 
|---|
|  | 382 | end; | 
|---|
|  | 383 | end; | 
|---|
|  | 384 | end; | 
|---|
|  | 385 | if FRequiredFilesFound then | 
|---|
|  | 386 | begin | 
|---|
|  | 387 | FMasterApp := Path + JAWSMasterApp; | 
|---|
|  | 388 | FRequiredFilesFound := FileExists(FMasterApp); | 
|---|
|  | 389 | end; | 
|---|
|  | 390 | if FRequiredFilesFound then | 
|---|
|  | 391 | begin | 
|---|
|  | 392 | FDefaultScriptDir := lowercase(GetPathFromJAWS(JAWS_PATH_ID_JAWS_DEFAULT_SCRIPT_FILES)); | 
|---|
|  | 393 | FRequiredFilesFound := (pos(JAWS_COMMON_SCRIPT_PATH_TEXT, FDefaultScriptDir) > 0); | 
|---|
|  | 394 | end; | 
|---|
|  | 395 | if FRequiredFilesFound then | 
|---|
|  | 396 | begin | 
|---|
|  | 397 | FUserStriptDir := lowercase(GetPathFromJAWS(JAWS_PATH_ID_USER_SCRIPT_FILES)); | 
|---|
|  | 398 | FRequiredFilesFound := (pos(JAWS_COMMON_SCRIPT_PATH_TEXT, FUserStriptDir) > 0); | 
|---|
|  | 399 | end; | 
|---|
|  | 400 | end; | 
|---|
|  | 401 |  | 
|---|
|  | 402 | begin | 
|---|
|  | 403 | FindCompiler; | 
|---|
|  | 404 | if FCompiler <> '' then | 
|---|
|  | 405 | FindJAWSRequiredFiles; | 
|---|
|  | 406 | end; | 
|---|
|  | 407 |  | 
|---|
|  | 408 | destructor TJAWSManager.Destroy; | 
|---|
|  | 409 | begin | 
|---|
|  | 410 | ShutDown; | 
|---|
|  | 411 | inherited; | 
|---|
|  | 412 | end; | 
|---|
|  | 413 |  | 
|---|
|  | 414 | function TJAWSManager.FileErrorExists: boolean; | 
|---|
|  | 415 | begin | 
|---|
|  | 416 | Result := (FJAWSFileError <> ''); | 
|---|
|  | 417 | end; | 
|---|
|  | 418 |  | 
|---|
|  | 419 |  | 
|---|
|  | 420 | class function TJAWSManager.GetJAWSWindow: HWnd; | 
|---|
|  | 421 | const | 
|---|
|  | 422 | VISIBLE_WINDOW_CLASS: PChar = 'JFWUI2'; | 
|---|
|  | 423 | VISIBLE_WINDOW_TITLE: PChar = 'JAWS'; | 
|---|
|  | 424 | VISIBLE_WINDOW_TITLE2: PChar = 'Remote JAWS'; | 
|---|
|  | 425 |  | 
|---|
|  | 426 | begin | 
|---|
|  | 427 | Result := FindWindow(VISIBLE_WINDOW_CLASS, VISIBLE_WINDOW_TITLE); | 
|---|
|  | 428 | if Result = 0 then | 
|---|
|  | 429 | Result := FindWindow(VISIBLE_WINDOW_CLASS, VISIBLE_WINDOW_TITLE2); | 
|---|
|  | 430 | end; | 
|---|
|  | 431 |  | 
|---|
|  | 432 | function TJAWSManager.Initialize(ComponentCallBackProc: TComponentDataRequestProc): BOOL; | 
|---|
|  | 433 | var | 
|---|
|  | 434 | DestPath: string; | 
|---|
|  | 435 | ScriptFileChanges: boolean; | 
|---|
|  | 436 | LastFileUpdated: boolean; | 
|---|
|  | 437 | CompileCommands: TStringList; | 
|---|
|  | 438 | AppScriptNeedsFunction: boolean; | 
|---|
|  | 439 | AppNeedsUseLine: boolean; | 
|---|
|  | 440 | AppUseLine: string; | 
|---|
|  | 441 | AppStartFunctionLine: integer; | 
|---|
|  | 442 |  | 
|---|
|  | 443 | procedure EnsureWindow; | 
|---|
|  | 444 | begin | 
|---|
|  | 445 | if not assigned(FMainForm) then | 
|---|
|  | 446 | FMainForm := TfrmVA508HiddenJawsMainWindow.Create(nil); | 
|---|
|  | 447 | FMainForm.ComponentDataCallBackProc := ComponentCallBackProc; | 
|---|
|  | 448 | FMainForm.ConfigReloadProc := ReloadConfiguration; | 
|---|
|  | 449 | FMainForm.HandleNeeded; | 
|---|
|  | 450 | Application.ProcessMessages; | 
|---|
|  | 451 | end; | 
|---|
|  | 452 |  | 
|---|
|  | 453 |  | 
|---|
|  | 454 | function GetVersion(FileName: string): integer; | 
|---|
|  | 455 | var | 
|---|
|  | 456 | list: TStringList; | 
|---|
|  | 457 |  | 
|---|
|  | 458 | p,i: integer; | 
|---|
|  | 459 | line: string; | 
|---|
|  | 460 | working: boolean; | 
|---|
|  | 461 | begin | 
|---|
|  | 462 | Result := 0; | 
|---|
|  | 463 | list := TStringList.Create; | 
|---|
|  | 464 | try | 
|---|
|  | 465 | list.LoadFromFile(FileName); | 
|---|
|  | 466 | i := 0; | 
|---|
|  | 467 | working := TRUE; | 
|---|
|  | 468 | while working and (i < list.Count) do | 
|---|
|  | 469 | begin | 
|---|
|  | 470 | line := list[i]; | 
|---|
|  | 471 | p := pos('=', line); | 
|---|
|  | 472 | if p > 0 then | 
|---|
|  | 473 | begin | 
|---|
|  | 474 | if trim(copy(line,1,p-1)) = JAWS_SCRIPT_VERSION then | 
|---|
|  | 475 | begin | 
|---|
|  | 476 | line := trim(copy(line,p+1,MaxInt)); | 
|---|
|  | 477 | if copy(line,length(line), 1) = ',' then | 
|---|
|  | 478 | delete(line,length(line),1); | 
|---|
|  | 479 | Result := StrToIntDef(line, 0); | 
|---|
|  | 480 | working := FALSE; | 
|---|
|  | 481 | end; | 
|---|
|  | 482 | end; | 
|---|
|  | 483 | inc(i); | 
|---|
|  | 484 | end; | 
|---|
|  | 485 | finally | 
|---|
|  | 486 | list.Free; | 
|---|
|  | 487 | end; | 
|---|
|  | 488 | end; | 
|---|
|  | 489 |  | 
|---|
|  | 490 | function VersionDifferent(FromFile, ToFile: string): boolean; | 
|---|
|  | 491 | var | 
|---|
|  | 492 | FromVersion, ToVersion: integer; | 
|---|
|  | 493 | begin | 
|---|
|  | 494 | FromVersion := GetVersion(FromFile); | 
|---|
|  | 495 | ToVersion := GetVersion(ToFile); | 
|---|
|  | 496 | Result := (FromVersion > ToVersion); | 
|---|
|  | 497 | end; | 
|---|
|  | 498 |  | 
|---|
|  | 499 | function LineItemUpdateNeeded(FromFile, ToFile: string): boolean; | 
|---|
|  | 500 | var | 
|---|
|  | 501 | fromList, toList: TStringList; | 
|---|
|  | 502 | i, idx: integer; | 
|---|
|  | 503 | line: string; | 
|---|
|  | 504 | begin | 
|---|
|  | 505 | Result := FALSE; | 
|---|
|  | 506 | fromList := TStringList.Create; | 
|---|
|  | 507 | toList := TStringList.Create; | 
|---|
|  | 508 | try | 
|---|
|  | 509 | fromList.LoadFromFile(FromFile); | 
|---|
|  | 510 | toList.LoadFromFile(toFile); | 
|---|
|  | 511 | for i := 0 to fromList.Count - 1 do | 
|---|
|  | 512 | begin | 
|---|
|  | 513 | line := fromList[i]; | 
|---|
|  | 514 | if trim(line) <> '' then | 
|---|
|  | 515 | begin | 
|---|
|  | 516 | idx := toList.IndexOf(line); | 
|---|
|  | 517 | if idx < 0 then | 
|---|
|  | 518 | begin | 
|---|
|  | 519 | Result := TRUE; | 
|---|
|  | 520 | break; | 
|---|
|  | 521 | end; | 
|---|
|  | 522 | end; | 
|---|
|  | 523 | end; | 
|---|
|  | 524 | finally | 
|---|
|  | 525 | toList.Free; | 
|---|
|  | 526 | fromList.Free; | 
|---|
|  | 527 | end; | 
|---|
|  | 528 | end; | 
|---|
|  | 529 |  | 
|---|
|  | 530 | function INIUpdateNeeded(FromFile, ToFile: string): boolean; | 
|---|
|  | 531 | var | 
|---|
|  | 532 | FromINIFile, ToINIFile: TIniFile; | 
|---|
|  | 533 | Sections, Values: TStringList; | 
|---|
|  | 534 | i, j: integer; | 
|---|
|  | 535 | section, key, val1, val2: string; | 
|---|
|  | 536 | begin | 
|---|
|  | 537 | Result := FALSE; | 
|---|
|  | 538 | Sections := TStringList.Create; | 
|---|
|  | 539 | Values := TStringList.Create; | 
|---|
|  | 540 | try | 
|---|
|  | 541 | FromINIFile := TIniFile.Create(FromFile); | 
|---|
|  | 542 | try | 
|---|
|  | 543 | ToINIFile := TIniFile.Create(ToFile); | 
|---|
|  | 544 | try | 
|---|
|  | 545 | FromINIFile.ReadSections(Sections); | 
|---|
|  | 546 | for i := 0 to Sections.count-1 do | 
|---|
|  | 547 | begin | 
|---|
|  | 548 | section := Sections[i]; | 
|---|
|  | 549 | FromINIFile.ReadSectionValues(section, Values); | 
|---|
|  | 550 | for j := 0 to Values.Count - 1 do | 
|---|
|  | 551 | begin | 
|---|
|  | 552 | key := Values.Names[j]; | 
|---|
|  | 553 | val1 := Values.ValueFromIndex[j]; | 
|---|
|  | 554 | val2 := ToINIFile.ReadString(Section, key, ''); | 
|---|
|  | 555 | result := (val1 <> val2); | 
|---|
|  | 556 | if Result then | 
|---|
|  | 557 | break; | 
|---|
|  | 558 | end; | 
|---|
|  | 559 | if Result then | 
|---|
|  | 560 | break; | 
|---|
|  | 561 | end; | 
|---|
|  | 562 | finally | 
|---|
|  | 563 | ToINIFile.Free; | 
|---|
|  | 564 | end; | 
|---|
|  | 565 | finally | 
|---|
|  | 566 | FromINIFile.Free; | 
|---|
|  | 567 | end; | 
|---|
|  | 568 | finally | 
|---|
|  | 569 | Sections.Free; | 
|---|
|  | 570 | Values.Free; | 
|---|
|  | 571 | end; | 
|---|
|  | 572 | end; | 
|---|
|  | 573 |  | 
|---|
|  | 574 | function IsUseLine(data: string): boolean; | 
|---|
|  | 575 | var | 
|---|
|  | 576 | p: integer; | 
|---|
|  | 577 | begin | 
|---|
|  | 578 | Result := (copy(data,1,4) = 'use '); | 
|---|
|  | 579 | if Result then | 
|---|
|  | 580 | begin | 
|---|
|  | 581 | Result := FALSE; | 
|---|
|  | 582 | p := pos('"', data); | 
|---|
|  | 583 | if p > 0 then | 
|---|
|  | 584 | begin | 
|---|
|  | 585 | p := posEX('"', data, p+1); | 
|---|
|  | 586 | if p = length(data) then | 
|---|
|  | 587 | Result := TRUE; | 
|---|
|  | 588 | end; | 
|---|
|  | 589 | end; | 
|---|
|  | 590 | end; | 
|---|
|  | 591 |  | 
|---|
|  | 592 | function IsFunctionLine(data: string): boolean; | 
|---|
|  | 593 | var | 
|---|
|  | 594 | p1, p2: integer; | 
|---|
|  | 595 | line: string; | 
|---|
|  | 596 | begin | 
|---|
|  | 597 | Result := FALSE; | 
|---|
|  | 598 | line := data; | 
|---|
|  | 599 | p1 := pos(' ', line); | 
|---|
|  | 600 | if (p1 > 0) then | 
|---|
|  | 601 | begin | 
|---|
|  | 602 | if copy(line,1,p1-1) = 'script' then | 
|---|
|  | 603 | Result := true | 
|---|
|  | 604 | else | 
|---|
|  | 605 | begin | 
|---|
|  | 606 | p2 := posEx(' ', line, p1+1); | 
|---|
|  | 607 | if p2 > 0 then | 
|---|
|  | 608 | begin | 
|---|
|  | 609 | line := copy(line, p1+1, p2-p1-1); | 
|---|
|  | 610 | if (line = 'function') then | 
|---|
|  | 611 | Result := TRUE; | 
|---|
|  | 612 | end; | 
|---|
|  | 613 | end; | 
|---|
|  | 614 | end; | 
|---|
|  | 615 | end; | 
|---|
|  | 616 |  | 
|---|
|  | 617 | function CheckForUseLineAndFunction(FromFile, ToFile: string): boolean; | 
|---|
|  | 618 | var | 
|---|
|  | 619 | FromData: TStringList; | 
|---|
|  | 620 | ToData: TStringList; | 
|---|
|  | 621 | UseLine: string; | 
|---|
|  | 622 | i: integer; | 
|---|
|  | 623 | line: string; | 
|---|
|  | 624 |  | 
|---|
|  | 625 | begin | 
|---|
|  | 626 | Result := FALSE; | 
|---|
|  | 627 | FromData := TStringList.create; | 
|---|
|  | 628 | ToData := TStringList.create; | 
|---|
|  | 629 | try | 
|---|
|  | 630 | UseLine := ''; | 
|---|
|  | 631 | AppUseLine := ''; | 
|---|
|  | 632 | AppStartFunctionLine := -1; | 
|---|
|  | 633 | FromData.LoadFromFile(FromFile); | 
|---|
|  | 634 | for i := 0 to FromData.Count - 1 do | 
|---|
|  | 635 | begin | 
|---|
|  | 636 | line := lowerCase(trim(FromData[i])); | 
|---|
|  | 637 | if (UseLine = '') and IsUseLine(line) then | 
|---|
|  | 638 | begin | 
|---|
|  | 639 | UseLine := line; | 
|---|
|  | 640 | AppUseLine := FromData[i]; | 
|---|
|  | 641 | end | 
|---|
|  | 642 | else | 
|---|
|  | 643 | if (AppStartFunctionLine < 0) and IsFunctionLine(line) then | 
|---|
|  | 644 | AppStartFunctionLine := i; | 
|---|
|  | 645 | if (UseLine <> '') and (AppStartFunctionLine >= 0) then break; | 
|---|
|  | 646 | end; | 
|---|
|  | 647 | if (UseLine = '') or (AppStartFunctionLine < 0) then exit; | 
|---|
|  | 648 |  | 
|---|
|  | 649 | AppNeedsUseLine := TRUE; | 
|---|
|  | 650 | AppScriptNeedsFunction := TRUE; | 
|---|
|  | 651 | ToData.LoadFromFile(ToFile); | 
|---|
|  | 652 | for i := 0 to ToData.Count - 1 do | 
|---|
|  | 653 | begin | 
|---|
|  | 654 | line := lowerCase(trim(ToData[i])); | 
|---|
|  | 655 | if AppNeedsUseLine and IsUseLine(line) and (line = UseLine) then | 
|---|
|  | 656 | AppNeedsUseLine := FALSE | 
|---|
|  | 657 | else | 
|---|
|  | 658 | if AppScriptNeedsFunction and IsFunctionLine(line) then | 
|---|
|  | 659 | AppScriptNeedsFunction := FALSE; | 
|---|
|  | 660 | if (not AppNeedsUseLine) and (not AppScriptNeedsFunction) then break; | 
|---|
|  | 661 | end; | 
|---|
|  | 662 | if AppNeedsUseLine or AppScriptNeedsFunction then | 
|---|
|  | 663 | Result := TRUE; | 
|---|
|  | 664 | finally | 
|---|
|  | 665 | FromData.free; | 
|---|
|  | 666 | ToData.free; | 
|---|
|  | 667 | end; | 
|---|
|  | 668 | end; | 
|---|
|  | 669 |  | 
|---|
|  | 670 | function UpdateNeeded(FromFile, ToFile: string; CompareType: TCompareType): boolean; | 
|---|
|  | 671 | begin | 
|---|
|  | 672 | Result := TRUE; | 
|---|
|  | 673 | try | 
|---|
|  | 674 | case CompareType of | 
|---|
|  | 675 | jcScriptMerge: Result := CheckForUseLineAndFunction(FromFile, ToFile); | 
|---|
|  | 676 | jcPrior:       Result := LastFileUpdated; | 
|---|
|  | 677 | jcVersion:     Result := VersionDifferent(FromFile, ToFile); | 
|---|
|  | 678 | jcINI:         Result := INIUpdateNeeded(FromFile, ToFile); | 
|---|
|  | 679 | jcLineItems:   Result := LineItemUpdateNeeded(FromFile, ToFile); | 
|---|
|  | 680 | end; | 
|---|
|  | 681 | except | 
|---|
|  | 682 | on E: Exception do | 
|---|
|  | 683 | FJAWSFileError := E.Message; | 
|---|
|  | 684 | end; | 
|---|
|  | 685 | end; | 
|---|
|  | 686 |  | 
|---|
|  | 687 | procedure INIFileUpdate(FromFile, ToFile: String); | 
|---|
|  | 688 | var | 
|---|
|  | 689 | FromINIFile, ToINIFile: TIniFile; | 
|---|
|  | 690 | Sections, Values: TStringList; | 
|---|
|  | 691 | i, j: integer; | 
|---|
|  | 692 | section, key, val1, val2: string; | 
|---|
|  | 693 | modified: boolean; | 
|---|
|  | 694 | begin | 
|---|
|  | 695 | modified := FALSE; | 
|---|
|  | 696 | Sections := TStringList.Create; | 
|---|
|  | 697 | Values := TStringList.Create; | 
|---|
|  | 698 | try | 
|---|
|  | 699 | FromINIFile := TIniFile.Create(FromFile); | 
|---|
|  | 700 | try | 
|---|
|  | 701 | ToINIFile := TIniFile.Create(ToFile); | 
|---|
|  | 702 | try | 
|---|
|  | 703 | FromINIFile.ReadSections(Sections); | 
|---|
|  | 704 | for i := 0 to Sections.count-1 do | 
|---|
|  | 705 | begin | 
|---|
|  | 706 | section := Sections[i]; | 
|---|
|  | 707 | FromINIFile.ReadSectionValues(section, Values); | 
|---|
|  | 708 | for j := 0 to Values.Count - 1 do | 
|---|
|  | 709 | begin | 
|---|
|  | 710 | key := Values.Names[j]; | 
|---|
|  | 711 | val1 := Values.ValueFromIndex[j]; | 
|---|
|  | 712 | val2 := ToINIFile.ReadString(Section, key, ''); | 
|---|
|  | 713 | if (val1 <> val2) then | 
|---|
|  | 714 | begin | 
|---|
|  | 715 | ToINIFile.WriteString(section, key, val1); | 
|---|
|  | 716 | modified := TRUE; | 
|---|
|  | 717 | end; | 
|---|
|  | 718 | end; | 
|---|
|  | 719 | end; | 
|---|
|  | 720 | finally | 
|---|
|  | 721 | if modified then | 
|---|
|  | 722 | ToINIFile.UpdateFile(); | 
|---|
|  | 723 | ToINIFile.Free; | 
|---|
|  | 724 | end; | 
|---|
|  | 725 | finally | 
|---|
|  | 726 | FromINIFile.Free; | 
|---|
|  | 727 | end; | 
|---|
|  | 728 | finally | 
|---|
|  | 729 | Sections.Free; | 
|---|
|  | 730 | Values.Free; | 
|---|
|  | 731 | end; | 
|---|
|  | 732 | end; | 
|---|
|  | 733 |  | 
|---|
|  | 734 | procedure LineItemFileUpdate(FromFile, ToFile: string); | 
|---|
|  | 735 | var | 
|---|
|  | 736 | fromList, toList: TStringList; | 
|---|
|  | 737 | i, idx: integer; | 
|---|
|  | 738 | line: string; | 
|---|
|  | 739 | modified: boolean; | 
|---|
|  | 740 | begin | 
|---|
|  | 741 | modified := FALSE; | 
|---|
|  | 742 | fromList := TStringList.Create; | 
|---|
|  | 743 | toList := TStringList.Create; | 
|---|
|  | 744 | try | 
|---|
|  | 745 | fromList.LoadFromFile(FromFile); | 
|---|
|  | 746 | toList.LoadFromFile(toFile); | 
|---|
|  | 747 | for i := 0 to fromList.Count - 1 do | 
|---|
|  | 748 | begin | 
|---|
|  | 749 | line := fromList[i]; | 
|---|
|  | 750 | if trim(line) <> '' then | 
|---|
|  | 751 | begin | 
|---|
|  | 752 | idx := toList.IndexOf(line); | 
|---|
|  | 753 | if idx < 0 then | 
|---|
|  | 754 | begin | 
|---|
|  | 755 | toList.Add(line); | 
|---|
|  | 756 | modified := TRUE; | 
|---|
|  | 757 | end; | 
|---|
|  | 758 | end; | 
|---|
|  | 759 | end; | 
|---|
|  | 760 | finally | 
|---|
|  | 761 | if Modified then | 
|---|
|  | 762 | toList.SaveToFile(ToFile); | 
|---|
|  | 763 | toList.Free; | 
|---|
|  | 764 | fromList.Free; | 
|---|
|  | 765 | end; | 
|---|
|  | 766 | end; | 
|---|
|  | 767 |  | 
|---|
|  | 768 | procedure DeleteCompiledFile(ToFile: string); | 
|---|
|  | 769 | var | 
|---|
|  | 770 | CompiledFile: string; | 
|---|
|  | 771 | begin | 
|---|
|  | 772 | CompiledFile := copy(ToFile, 1, length(ToFile) - length(ExtractFileExt(ToFile))); | 
|---|
|  | 773 | CompiledFile := CompiledFile + CompiledScriptFileExtension; | 
|---|
|  | 774 | if FileExists(CompiledFile) then | 
|---|
|  | 775 | begin | 
|---|
|  | 776 | MakeFileWritable(CompiledFile); | 
|---|
|  | 777 | DeleteFile(PChar(CompiledFile)); | 
|---|
|  | 778 | end; | 
|---|
|  | 779 | end; | 
|---|
|  | 780 |  | 
|---|
|  | 781 | function DoScriptMerge(FromFile, ToFile: string): boolean; | 
|---|
|  | 782 | var | 
|---|
|  | 783 | BackupFile: string; | 
|---|
|  | 784 | FromData: TStringList; | 
|---|
|  | 785 | ToData: TStringList; | 
|---|
|  | 786 | i, idx: integer; | 
|---|
|  | 787 | ExitCode: integer; | 
|---|
|  | 788 | begin | 
|---|
|  | 789 | Result := TRUE; | 
|---|
|  | 790 | BackupFile := ToFile + '.BACKUP'; | 
|---|
|  | 791 | if FileExists(BackupFile) then | 
|---|
|  | 792 | begin | 
|---|
|  | 793 | MakeFileWritable(BackupFile); | 
|---|
|  | 794 | DeleteFile(PChar(BackupFile)); | 
|---|
|  | 795 | end; | 
|---|
|  | 796 | DeleteCompiledFile(ToFile); | 
|---|
|  | 797 | CopyFile(PChar(ToFile), PChar(BackupFile), FALSE); | 
|---|
|  | 798 | MakeFileWritable(ToFile); | 
|---|
|  | 799 | FromData := TStringList.create; | 
|---|
|  | 800 | ToData := TStringList.create; | 
|---|
|  | 801 | try | 
|---|
|  | 802 | ToData.LoadFromFile(ToFile); | 
|---|
|  | 803 | if AppNeedsUseLine then | 
|---|
|  | 804 | ToData.Insert(0, AppUseLine); | 
|---|
|  | 805 | if AppScriptNeedsFunction then | 
|---|
|  | 806 | begin | 
|---|
|  | 807 | FromData.LoadFromFile(FromFile); | 
|---|
|  | 808 | ToData.Insert(1,''); | 
|---|
|  | 809 | idx := 2; | 
|---|
|  | 810 | for i := AppStartFunctionLine to FromData.Count - 1 do | 
|---|
|  | 811 | begin | 
|---|
|  | 812 | ToData.Insert(idx, FromData[i]); | 
|---|
|  | 813 | inc(idx); | 
|---|
|  | 814 | end; | 
|---|
|  | 815 | ToData.Insert(idx,''); | 
|---|
|  | 816 | end; | 
|---|
|  | 817 | if not assigned(JAWSAPI) then | 
|---|
|  | 818 | JAWSAPI := CoJawsApi.Create; | 
|---|
|  | 819 | ToData.SaveToFile(ToFile); | 
|---|
|  | 820 | ExitCode := ExecuteAndWait('"' + FCompiler + '"', '"' + ToFile + '"'); | 
|---|
|  | 821 | JAWSAPI.StopSpeech; | 
|---|
|  | 822 | if ExitCode = 0 then // compile succeeded! | 
|---|
|  | 823 | ReloadConfiguration | 
|---|
|  | 824 | else | 
|---|
|  | 825 | Result := FALSE; // compile failed - just copy the new one | 
|---|
|  | 826 | finally | 
|---|
|  | 827 | FromData.free; | 
|---|
|  | 828 | ToData.free; | 
|---|
|  | 829 | end; | 
|---|
|  | 830 | end; | 
|---|
|  | 831 |  | 
|---|
|  | 832 | procedure UpdateFile(FromFile, ToFile: string; info: TFileInfo); | 
|---|
|  | 833 | var | 
|---|
|  | 834 | DoCopy: boolean; | 
|---|
|  | 835 | error: boolean; | 
|---|
|  | 836 | CheckOverwrite: boolean; | 
|---|
|  | 837 | begin | 
|---|
|  | 838 | DoCopy := FALSE; | 
|---|
|  | 839 | if FileExists(ToFile) then | 
|---|
|  | 840 | begin | 
|---|
|  | 841 | MakeFileWritable(ToFile); | 
|---|
|  | 842 | CheckOverwrite := TRUE; | 
|---|
|  | 843 | try | 
|---|
|  | 844 | case info.CompareType of | 
|---|
|  | 845 | jcScriptMerge: if not DoScriptMerge(FromFile, ToFile) then DoCopy := TRUE; | 
|---|
|  | 846 | jcPrior, jcVersion: DoCopy := TRUE; | 
|---|
|  | 847 | jcINI: INIFileUpdate(FromFile, ToFile); | 
|---|
|  | 848 | jcLineItems: LineItemFileUpdate(FromFile, ToFile); | 
|---|
|  | 849 | end; | 
|---|
|  | 850 | except | 
|---|
|  | 851 | on E: Exception do | 
|---|
|  | 852 | FJAWSFileError := E.Message; | 
|---|
|  | 853 | end; | 
|---|
|  | 854 | end | 
|---|
|  | 855 | else | 
|---|
|  | 856 | begin | 
|---|
|  | 857 | CheckOverwrite := FALSE; | 
|---|
|  | 858 | DoCopy := TRUE; | 
|---|
|  | 859 | end; | 
|---|
|  | 860 | if DoCopy then | 
|---|
|  | 861 | begin | 
|---|
|  | 862 | error := FALSE; | 
|---|
|  | 863 | if not CopyFile(PChar(FromFile), PChar(Tofile), FALSE) then | 
|---|
|  | 864 | error := TRUE; | 
|---|
|  | 865 | if (not error) and (not FileExists(ToFile)) then | 
|---|
|  | 866 | error := TRUE; | 
|---|
|  | 867 | if (not error) and CheckOverwrite and (info.CompareType <> jcPrior) and | 
|---|
|  | 868 | UpdateNeeded(FromFile, ToFile, info.CompareType) then | 
|---|
|  | 869 | error := TRUE; | 
|---|
|  | 870 | if error and (not FileErrorExists) then | 
|---|
|  | 871 | FJAWSFileError := 'Error copying "' + FromFile + '" to' + CRLF + '"' + ToFile + '".'; | 
|---|
|  | 872 | if (not error) and (info.Compile) then | 
|---|
|  | 873 | begin | 
|---|
|  | 874 | DeleteCompiledFile(ToFile); | 
|---|
|  | 875 | CompileCommands.Add('"' + ToFile + '"'); | 
|---|
|  | 876 | end; | 
|---|
|  | 877 | end; | 
|---|
|  | 878 | end; | 
|---|
|  | 879 |  | 
|---|
|  | 880 | procedure EnsureJAWSScriptsAreUpToDate; | 
|---|
|  | 881 | var | 
|---|
|  | 882 | DestFile, FromFile, ToFile, AppName, ext: string; | 
|---|
|  | 883 | idx1, idx2, i: integer; | 
|---|
|  | 884 | DoUpdate: boolean; | 
|---|
|  | 885 | info: TFileInfo; | 
|---|
|  | 886 |  | 
|---|
|  | 887 | begin | 
|---|
|  | 888 | AppName := ExtractFileName(ParamStr(0)); | 
|---|
|  | 889 | ext := ExtractFileExt(AppName); | 
|---|
|  | 890 | AppName := LeftStr(AppName, length(AppName) - Length(ext)); | 
|---|
|  | 891 | DestPath := ''; | 
|---|
|  | 892 | idx1 := pos(JAWS_COMMON_SCRIPT_PATH_TEXT, FUserStriptDir); | 
|---|
|  | 893 | idx2 := pos(JAWS_COMMON_SCRIPT_PATH_TEXT, FDefaultScriptDir); | 
|---|
|  | 894 | if (idx1 > 0) and (idx2 > 0) then | 
|---|
|  | 895 | begin | 
|---|
|  | 896 | DestPath := copy(FUserStriptDir,1,idx1-1) + copy(FDefaultScriptDir, idx2, MaxInt); | 
|---|
|  | 897 | DestFile := DestPath + AppName; | 
|---|
|  | 898 | FDictionaryFileName := DestFile + DictionaryFileExtension; | 
|---|
|  | 899 | FConfigFile := DestFile + ConfigFileExtension; | 
|---|
|  | 900 | FKeyMapFile := DestFile + KeyMapExtension; | 
|---|
|  | 901 | LastFileUpdated := FALSE; | 
|---|
|  | 902 | for i := low(FileInfo) to high(FileInfo) do | 
|---|
|  | 903 | begin | 
|---|
|  | 904 | info := FileInfo[i]; | 
|---|
|  | 905 | if info.AppFile then | 
|---|
|  | 906 | begin | 
|---|
|  | 907 | FromFile := FRootScriptAppFileName + info.Ext; | 
|---|
|  | 908 | ToFile := DestFile + info.Ext; | 
|---|
|  | 909 | end | 
|---|
|  | 910 | else | 
|---|
|  | 911 | begin | 
|---|
|  | 912 | FromFile := FRootScriptFileName + info.Ext; | 
|---|
|  | 913 | ToFile := DestPath + JAWS_SCRIPT_NAME + info.Ext; | 
|---|
|  | 914 | end; | 
|---|
|  | 915 | if not FileExists(FromFile) then continue; | 
|---|
|  | 916 | if FileExists(ToFile) then | 
|---|
|  | 917 | begin | 
|---|
|  | 918 | DoUpdate := UpdateNeeded(FromFile, ToFile, info.CompareType); | 
|---|
|  | 919 | if DoUpdate then | 
|---|
|  | 920 | MakeFileWritable(ToFile); | 
|---|
|  | 921 | end | 
|---|
|  | 922 | else | 
|---|
|  | 923 | DoUpdate := TRUE; | 
|---|
|  | 924 | LastFileUpdated := DoUpdate; | 
|---|
|  | 925 | if DoUpdate and (not FileErrorExists) then | 
|---|
|  | 926 | begin | 
|---|
|  | 927 | UpdateFile(FromFile, ToFile, info); | 
|---|
|  | 928 | ScriptFileChanges := TRUE; | 
|---|
|  | 929 | end; | 
|---|
|  | 930 | if FileErrorExists then | 
|---|
|  | 931 | break; | 
|---|
|  | 932 | end; | 
|---|
|  | 933 | end | 
|---|
|  | 934 | else | 
|---|
|  | 935 | FJAWSFileError := 'Unknown File Error'; // should never happen - condition checked previously | 
|---|
|  | 936 | end; | 
|---|
|  | 937 |  | 
|---|
|  | 938 | procedure DoCompiles; | 
|---|
|  | 939 | var | 
|---|
|  | 940 | i: integer; | 
|---|
|  | 941 | begin | 
|---|
|  | 942 | if not assigned(JAWSAPI) then | 
|---|
|  | 943 | JAWSAPI := CoJawsApi.Create; | 
|---|
|  | 944 | for i := 0 to CompileCommands.Count - 1 do | 
|---|
|  | 945 | begin | 
|---|
|  | 946 | ExecuteAndWait('"' + FCompiler + '"', CompileCommands[i]); | 
|---|
|  | 947 | JAWSAPI.StopSpeech; | 
|---|
|  | 948 | end; | 
|---|
|  | 949 | ReloadConfiguration; | 
|---|
|  | 950 | end; | 
|---|
|  | 951 |  | 
|---|
|  | 952 | begin | 
|---|
|  | 953 | Result := FALSE; | 
|---|
|  | 954 | ScriptFileChanges := FALSE; | 
|---|
|  | 955 | if JAWSManager.RequiredFilesFound then | 
|---|
|  | 956 | begin | 
|---|
|  | 957 | FJAWSFileError := ''; | 
|---|
|  | 958 | CompileCommands := TStringList.Create; | 
|---|
|  | 959 | try | 
|---|
|  | 960 | EnsureJAWSScriptsAreUpToDate; | 
|---|
|  | 961 | if CompileCommands.Count > 0 then | 
|---|
|  | 962 | DoCompiles; | 
|---|
|  | 963 | finally | 
|---|
|  | 964 | CompileCommands.Free; | 
|---|
|  | 965 | end; | 
|---|
|  | 966 | if FileErrorExists then | 
|---|
|  | 967 | ShowError(JAWS_ERROR_FILE_IO, [FJAWSFileError]) | 
|---|
|  | 968 | else if JAWSTalking2CurrentUser then | 
|---|
|  | 969 | begin | 
|---|
|  | 970 | EnsureWindow; | 
|---|
|  | 971 | LaunchMasterApplication; | 
|---|
|  | 972 | if ScriptFileChanges then | 
|---|
|  | 973 | begin | 
|---|
|  | 974 | FMainForm.ConfigReloadNeeded; | 
|---|
|  | 975 | end; | 
|---|
|  | 976 | Result := TRUE; | 
|---|
|  | 977 | end; | 
|---|
|  | 978 | end; | 
|---|
|  | 979 | end; | 
|---|
|  | 980 |  | 
|---|
|  | 981 | class function TJAWSManager.IsRunning(HighVersion, LowVersion: Word): BOOL; | 
|---|
|  | 982 |  | 
|---|
|  | 983 | function ComponentVersionSupported: boolean; | 
|---|
|  | 984 | var | 
|---|
|  | 985 | SupportedHighVersion, SupportedLowVersion: integer; | 
|---|
|  | 986 | FileName, newVersion, convertedVersion, currentVersion: string; | 
|---|
|  | 987 | addr: pointer; | 
|---|
|  | 988 |  | 
|---|
|  | 989 | begin | 
|---|
|  | 990 | addr := @TJAWSManager.IsRunning; | 
|---|
|  | 991 | FileName := GetDLLFileName(addr); | 
|---|
|  | 992 | currentVersion := FileVersionValue(FileName, FILE_VER_FILEVERSION); | 
|---|
|  | 993 | VersionStringSplit(currentVersion, SupportedHighVersion, SupportedLowVersion); | 
|---|
|  | 994 | Result := FALSE; | 
|---|
|  | 995 | if (HighVersion < SupportedHighVersion) then | 
|---|
|  | 996 | Result := TRUE | 
|---|
|  | 997 | else | 
|---|
|  | 998 | if (HighVersion = SupportedHighVersion) and | 
|---|
|  | 999 | (LowVersion <= SupportedLowVersion) then | 
|---|
|  | 1000 | Result := TRUE; | 
|---|
|  | 1001 | if not Result then | 
|---|
|  | 1002 | begin | 
|---|
|  | 1003 | newVersion := IntToStr(HighVersion) + '.' + IntToStr(LowVersion); | 
|---|
|  | 1004 | convertedVersion := IntToStr(SupportedHighVersion) + '.' + IntToStr(SupportedLowVersion); | 
|---|
|  | 1005 | ShowError(DLL_ERROR_VERSION, [newVersion, convertedVersion]); | 
|---|
|  | 1006 | end; | 
|---|
|  | 1007 | end; | 
|---|
|  | 1008 |  | 
|---|
|  | 1009 | begin | 
|---|
|  | 1010 | Result := (GetJAWSWindow <> 0); | 
|---|
|  | 1011 | if Result then | 
|---|
|  | 1012 | Result := ComponentVersionSupported; | 
|---|
|  | 1013 | if Result then | 
|---|
|  | 1014 | Result := JAWSVersionOK; | 
|---|
|  | 1015 | if Result then | 
|---|
|  | 1016 | begin | 
|---|
|  | 1017 | EnsureManager; | 
|---|
|  | 1018 | with JAWSManager do | 
|---|
|  | 1019 | Result := RequiredFilesFound; | 
|---|
|  | 1020 | end; | 
|---|
|  | 1021 | end; | 
|---|
|  | 1022 |  | 
|---|
|  | 1023 | class function TJAWSManager.JAWSTalking2CurrentUser: boolean; | 
|---|
|  | 1024 | var | 
|---|
|  | 1025 | CurrentUserPath: string; | 
|---|
|  | 1026 | WhatJAWSThinks: string; | 
|---|
|  | 1027 |  | 
|---|
|  | 1028 | procedure Fix(var path: string); | 
|---|
|  | 1029 | var | 
|---|
|  | 1030 | idx: integer; | 
|---|
|  | 1031 | begin | 
|---|
|  | 1032 | idx := pos(APP_DATA, lowercase(path)); | 
|---|
|  | 1033 | if idx > 0 then | 
|---|
|  | 1034 | path := LeftStr(path,idx-1); | 
|---|
|  | 1035 | idx := length(path); | 
|---|
|  | 1036 | while (idx > 0) and (path[idx] <> '\') do dec(idx); | 
|---|
|  | 1037 | delete(path,1,idx); | 
|---|
|  | 1038 | end; | 
|---|
|  | 1039 |  | 
|---|
|  | 1040 | function UserProblemExists: boolean; | 
|---|
|  | 1041 | begin | 
|---|
|  | 1042 | CurrentUserPath := GetSpecialFolderPath(CSIDL_APPDATA); | 
|---|
|  | 1043 | WhatJAWSThinks := GetPathFromJAWS(JAWS_PATH_ID_USER_SCRIPT_FILES, FALSE); | 
|---|
|  | 1044 | fix(CurrentUserPath); | 
|---|
|  | 1045 | fix(WhatJAWSThinks); | 
|---|
|  | 1046 | Result := (lowercase(CurrentUserPath) <> lowercase(WhatJAWSThinks)); | 
|---|
|  | 1047 | end; | 
|---|
|  | 1048 |  | 
|---|
|  | 1049 | begin | 
|---|
|  | 1050 | if UserProblemExists then | 
|---|
|  | 1051 | begin | 
|---|
|  | 1052 | ShowError(JAWS_ERROR_USER_PROBLEM); | 
|---|
|  | 1053 | Result := FALSE; | 
|---|
|  | 1054 | end | 
|---|
|  | 1055 | else | 
|---|
|  | 1056 | Result := TRUE; | 
|---|
|  | 1057 | end; | 
|---|
|  | 1058 |  | 
|---|
|  | 1059 | class function TJAWSManager.JAWSVersionOK: boolean; | 
|---|
|  | 1060 | var | 
|---|
|  | 1061 | JFileVersion: string; | 
|---|
|  | 1062 | JFile: string; | 
|---|
|  | 1063 |  | 
|---|
|  | 1064 | function OlderVersionOKIfCOMObjectInstalled: boolean; | 
|---|
|  | 1065 | var | 
|---|
|  | 1066 | api: IJawsApi; | 
|---|
|  | 1067 | begin | 
|---|
|  | 1068 | Result := VersionOK(JAWS_REQUIRED_VERSION, JFileVersion); | 
|---|
|  | 1069 | if Result then | 
|---|
|  | 1070 | begin | 
|---|
|  | 1071 | try | 
|---|
|  | 1072 | try | 
|---|
|  | 1073 | api := CoJawsApi.Create; | 
|---|
|  | 1074 | except | 
|---|
|  | 1075 | Result := FALSE; | 
|---|
|  | 1076 | end; | 
|---|
|  | 1077 | finally | 
|---|
|  | 1078 | api := nil; | 
|---|
|  | 1079 | end; | 
|---|
|  | 1080 | end; | 
|---|
|  | 1081 | end; | 
|---|
|  | 1082 |  | 
|---|
|  | 1083 | begin | 
|---|
|  | 1084 | JFile := GetPathFromJAWS(JAWS_PATH_ID_APPLICATION);//JAWS_PATH_ID_USER_SCRIPT_FILES); | 
|---|
|  | 1085 | JFile := AppendBackSlash(JFile) + JAWS_APPLICATION_FILENAME; | 
|---|
|  | 1086 | if FileExists(JFile) then | 
|---|
|  | 1087 | begin | 
|---|
|  | 1088 | JFileVersion := FileVersionValue(JFile, FILE_VER_FILEVERSION); | 
|---|
|  | 1089 | Result := VersionOK(JAWS_COM_OBJECT_VERSION, JFileVersion); | 
|---|
|  | 1090 | if not Result then | 
|---|
|  | 1091 | Result := OlderVersionOKIfCOMObjectInstalled; | 
|---|
|  | 1092 | end | 
|---|
|  | 1093 | else | 
|---|
|  | 1094 | begin | 
|---|
|  | 1095 | // if file not found, then assume a future version where the exe was moved | 
|---|
|  | 1096 | // to a different location | 
|---|
|  | 1097 | Result := TRUE; | 
|---|
|  | 1098 | end; | 
|---|
|  | 1099 | if not Result then | 
|---|
|  | 1100 | ShowError(JAWS_ERROR_VERSION); | 
|---|
|  | 1101 | end; | 
|---|
|  | 1102 |  | 
|---|
|  | 1103 | procedure TJAWSManager.KillINIFiles(Sender: TObject); | 
|---|
|  | 1104 | begin | 
|---|
|  | 1105 | if assigned(FDictionaryFile) then | 
|---|
|  | 1106 | begin | 
|---|
|  | 1107 | if FDictionaryFileModified then | 
|---|
|  | 1108 | begin | 
|---|
|  | 1109 | MakeFileWritable(FDictionaryFileName); | 
|---|
|  | 1110 | FDictionaryFile.SaveToFile(FDictionaryFileName); | 
|---|
|  | 1111 | end; | 
|---|
|  | 1112 | FreeAndNil(FDictionaryFile); | 
|---|
|  | 1113 | end; | 
|---|
|  | 1114 |  | 
|---|
|  | 1115 | if assigned(FConfigINIFile) then | 
|---|
|  | 1116 | begin | 
|---|
|  | 1117 | if FConfigINIFileModified then | 
|---|
|  | 1118 | begin | 
|---|
|  | 1119 | FConfigINIFile.UpdateFile; | 
|---|
|  | 1120 | end; | 
|---|
|  | 1121 | FreeAndNil(FConfigINIFile); | 
|---|
|  | 1122 | end; | 
|---|
|  | 1123 |  | 
|---|
|  | 1124 | if assigned(FKeyMapINIFile) then | 
|---|
|  | 1125 | begin | 
|---|
|  | 1126 | if FKeyMapINIFileModified then | 
|---|
|  | 1127 | begin | 
|---|
|  | 1128 | FKeyMapINIFile.UpdateFile; | 
|---|
|  | 1129 | end; | 
|---|
|  | 1130 | FreeAndNil(FKeyMapINIFile); | 
|---|
|  | 1131 | end; | 
|---|
|  | 1132 |  | 
|---|
|  | 1133 | if assigned(FAssignedKeys) then | 
|---|
|  | 1134 | FreeAndNil(FAssignedKeys); | 
|---|
|  | 1135 | end; | 
|---|
|  | 1136 |  | 
|---|
|  | 1137 | procedure TJAWSManager.LaunchMasterApplication; | 
|---|
|  | 1138 | begin | 
|---|
|  | 1139 | if FileExists(FMasterApp) then | 
|---|
|  | 1140 | ShellExecute(0, PChar('open'), PChar(FMasterApp), nil, | 
|---|
|  | 1141 | PChar(ExtractFilePath(FMasterApp)), SW_SHOWNA); | 
|---|
|  | 1142 | end; | 
|---|
|  | 1143 |  | 
|---|
|  | 1144 |  | 
|---|
|  | 1145 | procedure TJAWSManager.RegisterCustomBehavior(Before, After: string; | 
|---|
|  | 1146 | Action: integer); | 
|---|
|  | 1147 |  | 
|---|
|  | 1148 | const | 
|---|
|  | 1149 | WindowClassesSection = 'WindowClasses'; | 
|---|
|  | 1150 | MSAAClassesSection = 'MSAAClasses'; | 
|---|
|  | 1151 | DICT_DELIM: char = Char($2E); | 
|---|
|  | 1152 | CommonKeysSection = 'Common Keys'; | 
|---|
|  | 1153 | CustomCommandHelpSection = 'Custom Command Help'; | 
|---|
|  | 1154 | KeyCommand = 'VA508SendCustomCommand('; | 
|---|
|  | 1155 | KeyCommandLen = length(KeyCommand); | 
|---|
|  | 1156 |  | 
|---|
|  | 1157 | var | 
|---|
|  | 1158 | modified: boolean; | 
|---|
|  | 1159 |  | 
|---|
|  | 1160 | procedure Add2INIFile(var INIFile: TINIFile; var FileModified: boolean; | 
|---|
|  | 1161 | FileName, SectionName, Data, Value: string); | 
|---|
|  | 1162 | var | 
|---|
|  | 1163 | oldValue: string; | 
|---|
|  | 1164 |  | 
|---|
|  | 1165 | begin | 
|---|
|  | 1166 | if not assigned(INIFile) then | 
|---|
|  | 1167 | begin | 
|---|
|  | 1168 | MakeFileWritable(FileName); | 
|---|
|  | 1169 | INIFile := TINIFile.Create(FileName); | 
|---|
|  | 1170 | FileModified := FALSE; | 
|---|
|  | 1171 | end; | 
|---|
|  | 1172 | OldValue := INIFile.ReadString(SectionName, Data, ''); | 
|---|
|  | 1173 | if OldValue <> Value then | 
|---|
|  | 1174 | begin | 
|---|
|  | 1175 | INIFile.WriteString(SectionName, Data, Value); | 
|---|
|  | 1176 | modified := TRUE; | 
|---|
|  | 1177 | FileModified := TRUE; | 
|---|
|  | 1178 | end; | 
|---|
|  | 1179 | end; | 
|---|
|  | 1180 |  | 
|---|
|  | 1181 | procedure RemoveFromINIFile(var INIFile: TINIFile; var FileModified: boolean; | 
|---|
|  | 1182 | FileName, SectionName, Data: string); | 
|---|
|  | 1183 | var | 
|---|
|  | 1184 | oldValue: string; | 
|---|
|  | 1185 |  | 
|---|
|  | 1186 | begin | 
|---|
|  | 1187 | if not assigned(INIFile) then | 
|---|
|  | 1188 | begin | 
|---|
|  | 1189 | MakeFileWritable(FileName); | 
|---|
|  | 1190 | INIFile := TINIFile.Create(FileName); | 
|---|
|  | 1191 | FileModified := FALSE; | 
|---|
|  | 1192 | end; | 
|---|
|  | 1193 | OldValue := INIFile.ReadString(SectionName, Data, ''); | 
|---|
|  | 1194 | if OldValue <> '' then | 
|---|
|  | 1195 | begin | 
|---|
|  | 1196 | INIFile.DeleteKey(SectionName, Data); | 
|---|
|  | 1197 | modified := TRUE; | 
|---|
|  | 1198 | FileModified := TRUE; | 
|---|
|  | 1199 | end; | 
|---|
|  | 1200 | end; | 
|---|
|  | 1201 |  | 
|---|
|  | 1202 | procedure RegisterCustomClassChange; | 
|---|
|  | 1203 | begin | 
|---|
|  | 1204 | Add2INIFile(FConfigINIFile, FConfigINIFileModified, FConfigFile, | 
|---|
|  | 1205 | WindowClassesSection, Before, After); | 
|---|
|  | 1206 | end; | 
|---|
|  | 1207 |  | 
|---|
|  | 1208 | procedure RegisterMSAAClassChange; | 
|---|
|  | 1209 | begin | 
|---|
|  | 1210 | Add2INIFile(FConfigINIFile, FConfigINIFileModified, FConfigFile, | 
|---|
|  | 1211 | MSAAClassesSection, Before, '1'); | 
|---|
|  | 1212 | end; | 
|---|
|  | 1213 |  | 
|---|
|  | 1214 | procedure RegisterCustomKeyMapping; | 
|---|
|  | 1215 | begin | 
|---|
|  | 1216 | Add2INIFile(FKeyMapINIFile, FKeyMapINIFileModified, FKeyMapFile, | 
|---|
|  | 1217 | CommonKeysSection, Before, KeyCommand + after + ')'); | 
|---|
|  | 1218 | if not assigned(FAssignedKeys) then | 
|---|
|  | 1219 | FAssignedKeys := TStringList.Create; | 
|---|
|  | 1220 | FAssignedKeys.Add(Before); | 
|---|
|  | 1221 | end; | 
|---|
|  | 1222 |  | 
|---|
|  | 1223 | procedure RegisterCustomKeyDescription; | 
|---|
|  | 1224 | begin | 
|---|
|  | 1225 | Add2INIFile(FConfigINIFile, FConfigINIFileModified, FConfigFile, | 
|---|
|  | 1226 | CustomCommandHelpSection, Before, After); | 
|---|
|  | 1227 | end; | 
|---|
|  | 1228 |  | 
|---|
|  | 1229 | procedure DecodeLine(line: string; var before1, after1: string); | 
|---|
|  | 1230 | var | 
|---|
|  | 1231 | i, j, len: integer; | 
|---|
|  | 1232 | begin | 
|---|
|  | 1233 | before1 := ''; | 
|---|
|  | 1234 | after1 := ''; | 
|---|
|  | 1235 | len := length(line); | 
|---|
|  | 1236 | if (len < 2) or (line[1] <> DICT_DELIM) then exit; | 
|---|
|  | 1237 | i := 2; | 
|---|
|  | 1238 | while (i < len) and (line[i] <> DICT_DELIM) do inc(i); | 
|---|
|  | 1239 | before1 := copy(line,2,i-2); | 
|---|
|  | 1240 | j := i + 1; | 
|---|
|  | 1241 | while (j <= len) and (line[j] <> DICT_DELIM) do inc(j); | 
|---|
|  | 1242 | after1 := copy(line,i+1,j-i-1); | 
|---|
|  | 1243 | end; | 
|---|
|  | 1244 |  | 
|---|
|  | 1245 | procedure RegisterCustomDictionaryChange; | 
|---|
|  | 1246 | var | 
|---|
|  | 1247 | i, idx: integer; | 
|---|
|  | 1248 | line, before1, after1: string; | 
|---|
|  | 1249 | add: boolean; | 
|---|
|  | 1250 | begin | 
|---|
|  | 1251 | if not assigned(FDictionaryFile) then | 
|---|
|  | 1252 | begin | 
|---|
|  | 1253 | FDictionaryFile := TStringList.Create; | 
|---|
|  | 1254 | FDictionaryFileModified := FALSE; | 
|---|
|  | 1255 | if FileExists(FDictionaryFileName) then | 
|---|
|  | 1256 | FDictionaryFile.LoadFromFile(FDictionaryFileName); | 
|---|
|  | 1257 | end; | 
|---|
|  | 1258 |  | 
|---|
|  | 1259 | add := TRUE; | 
|---|
|  | 1260 | idx := -1; | 
|---|
|  | 1261 | for I := 0 to FDictionaryFile.Count - 1 do | 
|---|
|  | 1262 | begin | 
|---|
|  | 1263 | line := FDictionaryFile[i]; | 
|---|
|  | 1264 | DecodeLine(line, before1, after1); | 
|---|
|  | 1265 | if (before1 = Before) then | 
|---|
|  | 1266 | begin | 
|---|
|  | 1267 | idx := i; | 
|---|
|  | 1268 | if after1 = after then | 
|---|
|  | 1269 | add := false; | 
|---|
|  | 1270 | break; | 
|---|
|  | 1271 | end; | 
|---|
|  | 1272 | end; | 
|---|
|  | 1273 | if add then | 
|---|
|  | 1274 | begin | 
|---|
|  | 1275 | line := DICT_DELIM + Before + DICT_DELIM + after + DICT_DELIM; | 
|---|
|  | 1276 | if idx < 0 then | 
|---|
|  | 1277 | FDictionaryFile.Add(line) | 
|---|
|  | 1278 | else | 
|---|
|  | 1279 | FDictionaryFile[idx] := line; | 
|---|
|  | 1280 | modified := TRUE; | 
|---|
|  | 1281 | FDictionaryFileModified := TRUE; | 
|---|
|  | 1282 | end; | 
|---|
|  | 1283 | end; | 
|---|
|  | 1284 |  | 
|---|
|  | 1285 | procedure RemoveComponentClass; | 
|---|
|  | 1286 | begin | 
|---|
|  | 1287 | RemoveFromINIFile(FConfigINIFile, FConfigINIFileModified, FConfigFile, | 
|---|
|  | 1288 | WindowClassesSection, Before); | 
|---|
|  | 1289 | end; | 
|---|
|  | 1290 |  | 
|---|
|  | 1291 | procedure RemoveMSAAClass; | 
|---|
|  | 1292 | begin | 
|---|
|  | 1293 | RemoveFromINIFile(FConfigINIFile, FConfigINIFileModified, FConfigFile, | 
|---|
|  | 1294 | MSAAClassesSection, Before); | 
|---|
|  | 1295 | end; | 
|---|
|  | 1296 |  | 
|---|
|  | 1297 | procedure PurgeKeyMappings; | 
|---|
|  | 1298 | var | 
|---|
|  | 1299 | i: integer; | 
|---|
|  | 1300 | name, value: string; | 
|---|
|  | 1301 | keys: TStringList; | 
|---|
|  | 1302 | delete: boolean; | 
|---|
|  | 1303 | begin | 
|---|
|  | 1304 | if not assigned(FKeyMapINIFile) then | 
|---|
|  | 1305 | begin | 
|---|
|  | 1306 | MakeFileWritable(FKeyMapFile); | 
|---|
|  | 1307 | FKeyMapINIFile := TINIFile.Create(FKeyMapFile); | 
|---|
|  | 1308 | FKeyMapINIFileModified := FALSE; | 
|---|
|  | 1309 | end; | 
|---|
|  | 1310 | keys := TStringList.Create; | 
|---|
|  | 1311 | try | 
|---|
|  | 1312 | FKeyMapINIFile.ReadSectionValues(CommonKeysSection, keys); | 
|---|
|  | 1313 | for i := keys.Count - 1 downto 0 do | 
|---|
|  | 1314 | begin | 
|---|
|  | 1315 | value := copy(keys.ValueFromIndex[i],1,KeyCommandLen); | 
|---|
|  | 1316 | if value = KeyCommand then | 
|---|
|  | 1317 | begin | 
|---|
|  | 1318 | name := keys.Names[i]; | 
|---|
|  | 1319 | delete := (not assigned(FAssignedKeys)); | 
|---|
|  | 1320 | if not delete then | 
|---|
|  | 1321 | delete := (FAssignedKeys.IndexOf(name) < 0); | 
|---|
|  | 1322 | if delete then | 
|---|
|  | 1323 | begin | 
|---|
|  | 1324 | FKeyMapINIFile.DeleteKey(CommonKeysSection, name); | 
|---|
|  | 1325 | FKeyMapINIFileModified := TRUE; | 
|---|
|  | 1326 | modified := TRUE; | 
|---|
|  | 1327 | end; | 
|---|
|  | 1328 | end; | 
|---|
|  | 1329 | end; | 
|---|
|  | 1330 | finally | 
|---|
|  | 1331 | keys.Free; | 
|---|
|  | 1332 | end; | 
|---|
|  | 1333 | end; | 
|---|
|  | 1334 |  | 
|---|
|  | 1335 | begin | 
|---|
|  | 1336 | { TODO : check file io errors when updating config files } | 
|---|
|  | 1337 | modified := FALSE; | 
|---|
|  | 1338 | case Action of | 
|---|
|  | 1339 | BEHAVIOR_ADD_DICTIONARY_CHANGE:           RegisterCustomDictionaryChange; | 
|---|
|  | 1340 | BEHAVIOR_ADD_COMPONENT_CLASS:             RegisterCustomClassChange; | 
|---|
|  | 1341 | BEHAVIOR_ADD_COMPONENT_MSAA:              RegisterMSAAClassChange; | 
|---|
|  | 1342 | BEHAVIOR_ADD_CUSTOM_KEY_MAPPING:          RegisterCustomKeyMapping; | 
|---|
|  | 1343 | BEHAVIOR_ADD_CUSTOM_KEY_DESCRIPTION:      RegisterCustomKeyDescription; | 
|---|
|  | 1344 | BEHAVIOR_REMOVE_COMPONENT_CLASS:          RemoveComponentClass; | 
|---|
|  | 1345 | BEHAVIOR_REMOVE_COMPONENT_MSAA:           RemoveMSAAClass; | 
|---|
|  | 1346 | BEHAVIOR_PURGE_UNREGISTERED_KEY_MAPPINGS: PurgeKeyMappings; | 
|---|
|  | 1347 | end; | 
|---|
|  | 1348 | if modified and assigned(FMainForm) then | 
|---|
|  | 1349 | begin | 
|---|
|  | 1350 | FMainForm.ResetINITimer(KillINIFiles); | 
|---|
|  | 1351 | FMainForm.ConfigReloadNeeded; | 
|---|
|  | 1352 | end; | 
|---|
|  | 1353 | end; | 
|---|
|  | 1354 |  | 
|---|
|  | 1355 | procedure TJAWSManager.ReloadConfiguration; | 
|---|
|  | 1356 | begin | 
|---|
|  | 1357 | if not assigned(JAWSAPI) then | 
|---|
|  | 1358 | JAWSAPI := CoJawsApi.Create; | 
|---|
|  | 1359 | JAWSAPI.RunFunction('ReloadAllConfigs'); | 
|---|
|  | 1360 | end; | 
|---|
|  | 1361 |  | 
|---|
|  | 1362 | procedure TJAWSManager.SendComponentData(WindowHandle: HWND; DataStatus: LongInt; Caption, Value, | 
|---|
|  | 1363 | Data, ControlType, State, Instructions, ItemInstructions: PChar); | 
|---|
|  | 1364 |  | 
|---|
|  | 1365 | procedure SendRequestResponse; | 
|---|
|  | 1366 | begin | 
|---|
|  | 1367 | FMainForm.WriteData(VA508_REG_COMPONENT_CAPTION, Caption); | 
|---|
|  | 1368 | FMainForm.WriteData(VA508_REG_COMPONENT_VALUE, Value); | 
|---|
|  | 1369 | FMainForm.WriteData(VA508_REG_COMPONENT_CONTROL_TYPE, ControlType); | 
|---|
|  | 1370 | FMainForm.WriteData(VA508_REG_COMPONENT_STATE, State); | 
|---|
|  | 1371 | FMainForm.WriteData(VA508_REG_COMPONENT_INSTRUCTIONS, Instructions); | 
|---|
|  | 1372 | FMainForm.WriteData(VA508_REG_COMPONENT_ITEM_INSTRUCTIONS, ItemInstructions); | 
|---|
|  | 1373 | FMainForm.WriteData(VA508_REG_COMPONENT_DATA_STATUS, IntToStr(DataStatus)); | 
|---|
|  | 1374 | FMainForm.PostData; | 
|---|
|  | 1375 | end; | 
|---|
|  | 1376 |  | 
|---|
|  | 1377 | procedure SendChangeEvent; | 
|---|
|  | 1378 | var | 
|---|
|  | 1379 | Event: WideString; | 
|---|
|  | 1380 | begin | 
|---|
|  | 1381 | Event := 'VA508ChangeEvent(' + IntToStr(WindowHandle)   +  ',' + | 
|---|
|  | 1382 | IntToStr(DataStatus)     +  ',"' + | 
|---|
|  | 1383 | StrPas(Caption)          + '","' + | 
|---|
|  | 1384 | StrPas(Value)            + '","' + | 
|---|
|  | 1385 | StrPas(ControlType)      + '","' + | 
|---|
|  | 1386 | StrPas(State)            + '","' + | 
|---|
|  | 1387 | StrPas(Instructions)     + '","' + | 
|---|
|  | 1388 | StrPas(ItemInstructions) + '"'; | 
|---|
|  | 1389 | if not assigned(JAWSAPI) then | 
|---|
|  | 1390 | JAWSAPI := CoJawsApi.Create; | 
|---|
|  | 1391 | JAWSAPI.RunFunction(Event) | 
|---|
|  | 1392 | end; | 
|---|
|  | 1393 |  | 
|---|
|  | 1394 | begin | 
|---|
|  | 1395 | if (Data <> nil) and (Length(Data) > 0) then | 
|---|
|  | 1396 | begin | 
|---|
|  | 1397 | Value := Data; | 
|---|
|  | 1398 | DataStatus := DataStatus AND DATA_MASK_DATA; | 
|---|
|  | 1399 | DataStatus := DataStatus OR DATA_VALUE; | 
|---|
|  | 1400 | end; | 
|---|
|  | 1401 | if (DataStatus and DATA_CHANGE_EVENT) <> 0 then | 
|---|
|  | 1402 | begin | 
|---|
|  | 1403 | DataStatus := DataStatus AND DATA_MASK_CHANGE_EVENT; | 
|---|
|  | 1404 | SendChangeEvent; | 
|---|
|  | 1405 | end | 
|---|
|  | 1406 | else | 
|---|
|  | 1407 | SendRequestResponse; | 
|---|
|  | 1408 | end; | 
|---|
|  | 1409 |  | 
|---|
|  | 1410 | const | 
|---|
|  | 1411 | MAX_REG_CHARS = 125; // When Jaws reads over 126 chars it returns a blank string | 
|---|
|  | 1412 | MORE_STRINGS = '+'; | 
|---|
|  | 1413 | LAST_STRING = '-'; | 
|---|
|  | 1414 | MAX_COUNT_KEY = 'Max'; | 
|---|
|  | 1415 |  | 
|---|
|  | 1416 | class procedure TJAWSManager.ShowError(ErrorNumber: integer); | 
|---|
|  | 1417 | begin | 
|---|
|  | 1418 | ShowError(ErrorNumber, []); | 
|---|
|  | 1419 | end; | 
|---|
|  | 1420 |  | 
|---|
|  | 1421 | class procedure TJAWSManager.ShowError(ErrorNumber: integer; data: array of const); | 
|---|
|  | 1422 | var | 
|---|
|  | 1423 | error: string; | 
|---|
|  | 1424 |  | 
|---|
|  | 1425 | begin | 
|---|
|  | 1426 | if not JAWSErrorsShown[ErrorNumber] then | 
|---|
|  | 1427 | begin | 
|---|
|  | 1428 | error := JAWSErrorMessage[ErrorNumber]; | 
|---|
|  | 1429 | if length(data) > 0 then | 
|---|
|  | 1430 | error := Format(error, data); | 
|---|
|  | 1431 | JAWSErrorsShown[ErrorNumber] := TRUE; | 
|---|
|  | 1432 | MessageBox(0, PChar(error), 'JAWS Accessibility Component Error', | 
|---|
|  | 1433 | MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_TOPMOST); | 
|---|
|  | 1434 | end; | 
|---|
|  | 1435 | end; | 
|---|
|  | 1436 |  | 
|---|
|  | 1437 | procedure TJAWSManager.ShutDown; | 
|---|
|  | 1438 | begin | 
|---|
|  | 1439 | if FWasShutdown then exit; | 
|---|
|  | 1440 | if assigned(JAWSAPI) then | 
|---|
|  | 1441 | begin | 
|---|
|  | 1442 | try | 
|---|
|  | 1443 | JAWSAPI := nil; // causes access violation | 
|---|
|  | 1444 | except | 
|---|
|  | 1445 | end; | 
|---|
|  | 1446 | end; | 
|---|
|  | 1447 | KillINIFiles(nil); | 
|---|
|  | 1448 | if assigned(FMainForm) then | 
|---|
|  | 1449 | FreeAndNil(FMainForm); | 
|---|
|  | 1450 | FWasShutdown := TRUE; | 
|---|
|  | 1451 | end; | 
|---|
|  | 1452 |  | 
|---|
|  | 1453 | procedure TJAWSManager.SpeakText(Text: PChar); | 
|---|
|  | 1454 | begin | 
|---|
|  | 1455 | if not assigned(JAWSAPI) then | 
|---|
|  | 1456 | JAWSAPI := CoJawsApi.Create; | 
|---|
|  | 1457 | JAWSAPI.SayString(Text, FALSE); | 
|---|
|  | 1458 | end; | 
|---|
|  | 1459 |  | 
|---|
|  | 1460 |  | 
|---|
|  | 1461 | initialization | 
|---|
|  | 1462 | CoInitializeEx(nil, COINIT_APARTMENTTHREADED); | 
|---|
|  | 1463 |  | 
|---|
|  | 1464 | finalization | 
|---|
|  | 1465 | ShutDown; | 
|---|
|  | 1466 | CoUninitialize; | 
|---|
|  | 1467 |  | 
|---|
|  | 1468 | end. | 
|---|