unit fVA508HiddenJawsMainWindow;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Contnrs, ExtCtrls, ComObj, VA508AccessibilityConst, AppEvnts;

type
  TfrmVA508HiddenJawsMainWindow = class(TForm)
    ReloadTimer: TTimer;
    CloseINIFilesTimer: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ReloadTimerTimer(Sender: TObject);
    procedure CloseINIFilesTimerTimer(Sender: TObject);
  private
    FRootCaption: string;
    FPostWindow: HWnd;
    FCloseFilesEvent: TNotifyEvent;
    FData: string;
    FVariables: string;
    FDataWindowPool: TList;
    FDataWindows: TObjectList;
    FComponentDataCallBackProc: TComponentDataRequestProc;
    FConfigChangePending: boolean;
    FConfigReloadProc: TConfigReloadProc;
    procedure ConverAccelChars(var text: string);
  protected
    procedure WndProc(var Msg: TMessage); override;
  public
    procedure ConfigReloadNeeded;
    procedure WriteData(VarName, Value: string);
    procedure PostData;
    procedure ResetINITimer(Event: TNotifyEvent);
    property ConfigReloadProc: TConfigReloadProc read FConfigReloadProc write FConfigReloadProc;
    property ComponentDataCallBackProc: TComponentDataRequestProc read FComponentDataCallBackProc write FComponentDataCallBackProc;
    property ConfigChangePending: boolean read FConfigChangePending write FConfigChangePending;
  end;

var
  frmVA508HiddenJawsMainWindow: TfrmVA508HiddenJawsMainWindow;

implementation

uses JAWSCommon, fVA508HiddenJawsDataWindow, VAUtils;

{$R *.dfm}

procedure TfrmVA508HiddenJawsMainWindow.CloseINIFilesTimerTimer(
  Sender: TObject);
begin
  CloseINIFilesTimer.Enabled := FALSE;
  if assigned(FCloseFilesEvent) then
    FCloseFilesEvent(Sender);
end;

procedure TfrmVA508HiddenJawsMainWindow.ConfigReloadNeeded;
begin
  ReloadTimer.Enabled := FALSE;
  ReloadTimer.Enabled := TRUE;
  FConfigChangePending := TRUE;
end;

// MSAA messes up strings with & chars
procedure TfrmVA508HiddenJawsMainWindow.ConverAccelChars(var text: string);
var
  i: integer;
  convert: boolean;
begin
  repeat
    i := pos('&', text);
    if i > 0 then
    begin
      convert := (i = length(text));
      if not convert then
        convert := not (text[i+1] in ['A'..'Z','a'..'z','0'..'9']);
      delete(text,i,1);
      if convert then
        insert(' and ', text, i);      
    end;
  until i = 0;
end;

procedure TfrmVA508HiddenJawsMainWindow.FormCreate(Sender: TObject);
begin
  ErrorCheckClassName(Self, DLL_MAIN_WINDOW_CLASS);
  FRootCaption := DLL_WINDOW_TITLE;
  Caption := FRootCaption;
  FDataWindowPool := TList.Create;
  FDataWindows := TObjectList.Create;
end;

procedure TfrmVA508HiddenJawsMainWindow.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FDataWindowPool);
  FreeAndNil(FDataWindows);
end;

procedure TfrmVA508HiddenJawsMainWindow.ReloadTimerTimer(Sender: TObject);
begin
  ReloadTimer.Enabled := FALSE;
  if assigned(FConfigReloadProc) then
    FConfigReloadProc;
  FConfigChangePending := FALSE;
end;

procedure TfrmVA508HiddenJawsMainWindow.ResetINITimer(Event: TNotifyEvent);
begin
  FCloseFilesEvent := Event;
  CloseINIFilesTimer.Enabled := FALSE;
  CloseINIFilesTimer.Enabled := TRUE;  
end;

// data is in 2 strings
// list of variable names
// list of data
procedure TfrmVA508HiddenJawsMainWindow.WriteData(VarName, Value: string);
var
  offset, len: integer;
begin
  ConverAccelChars(Value);
  len := length(Value);
  if len > 0 then
  begin
    offset := length(FData);
    FData := FData + Value;
  end
  else
    offset := 0;
  FVariables := FVariables + DLL_WINDOW_DELIM + VarName + DLL_WINDOW_OFFSET +
                             IntToStr(offset) + DLL_WINDOW_LENGTH + IntToStr(len);
end;

// output
// caption:[next window handle]:varlen:var=offset,length:var=offset,len:data
// varlen = from first to last :

procedure TfrmVA508HiddenJawsMainWindow.PostData;
var
  DataWindow, LastWindow: TfrmVA508HiddenJawsDataWindow;
  Data, Output, RootOutput: string;
  Done, UpdateLastWindow: boolean;
  DataLen, Len, HandleIdx, AllowedChars, StartIndex: integer;

  procedure GetDataWindow;
  var
    idx: integer;
  begin
    if assigned(DataWindow) then
      LastWindow := DataWindow;
    if FDataWindowPool.Count > 0 then
    begin
      idx := FDataWindowPool.Count-1;
      DataWindow := TfrmVA508HiddenJawsDataWindow(FDataWindowPool[idx]);
      FDataWindowPool.Delete(idx);
    end
    else
    begin
      DataWindow := TfrmVA508HiddenJawsDataWindow.Create(Self);
      FDataWindows.Add(DataWindow);
      DataWindow.HandleNeeded;
      Application.ProcessMessages;
    end;
    if FPostWindow = 0 then
      FPostWindow := DataWindow.Handle;
  end;

begin
  LastWindow := nil;
  DataWindow := nil;
  FVariables := FVariables + DLL_WINDOW_DELIM;
  Len := length(FVariables);
  Data := IntToStr(Len) + FVariables + FData;
  DataLen := length(Data) + 1;
// Format = header  : next window handle : data
  RootOutput := FRootCaption + DLL_WINDOW_DELIM + DLL_WINDOW_DELIM;
  HandleIdx := length(RootOutput);
  AllowedChars := DLL_CAPTION_LIMIT - Length(RootOutput);
  StartIndex := 1;
  UpdateLastWindow := FALSE;
  repeat
    Done := TRUE;
    GetDataWindow;
    if UpdateLastWindow then
    begin
      Output := LastWindow.Caption;
      insert(IntToStr(DataWindow.handle), Output, HandleIdx);
      LastWindow.Caption := Output;
      UpdateLastWindow := FALSE;
    end;
    Len := DataLen - StartIndex;
    Output := RootOutput + copy(Data, StartIndex, AllowedChars);
    DataWindow.Caption := Output;
    if Len > AllowedChars then
    begin
      UpdateLastWindow := TRUE;
      Done := FALSE;
      inc(startIndex, AllowedChars);
    end;
  until Done;
end;

procedure TfrmVA508HiddenJawsMainWindow.WndProc(var Msg: TMessage);
var
  i: integer;
  data: string;
begin
  if Msg.Msg = MessageID then
  begin
    Msg.Result := 1;
    data := caption;
    try
  // make sure Delphi has finished creating the form.  If we dont, JAWS can detect the
  // window before Delphi has finished creating it - resulting in an access violation
      if assigned(Self) then
      begin
        try
          if assigned(FComponentDataCallBackProc) then
          begin
            FPostWindow := 0;
            FData := '';
            FVariables := '';
            FDataWindowPool.Assign(FDataWindows);
            try
              FComponentDataCallBackProc(Msg.WParam, Msg.LParam);
            except
            end;
            // clean up unused windows
            for i := 0 to FDataWindowPool.Count - 1 do
              TfrmVA508HiddenJawsDataWindow(FDataWindowPool[i]).Caption := '';
            SendReturnValue(Handle, FPostWindow);
          end;
        except
        end;
      end;
    finally
      if data = caption then
      begin
        SendReturnValue(Handle, 0);
      end;
    end;
  end
  else
    inherited WndProc(Msg);
end;

end.
