| 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. | 
|---|