unit UHelp; interface uses IniFiles, ULog, SysUtils; type TKeymap = record Key: array of string; //key-combination translated Text: string; //description text end; TSection = record name: string; Keys: array of TKeymap; end; TSub = record title: string; text: array of string; end; TKeyNames = record Key: array of string; end; TTextResultSection = record name: string; Keys: array of TKeyNames; KeyDescription: array of string; end; TTextResult = record Title: String; Description: String; Subs: array of TSub; Sections: array of TTextResultSection; end; TKeys = record Key: string; Translation: string; end; TEntry = record ID: string; Title: string; Description: string; Subs: array of TSub; Sections: array of TSection; end; TLanguageList = record Name: string; end; THelp = class private actualID: string; ScrollPos: double; Entry: array of TEntry; SEntry: array of TEntry; AEntry: TEntry; List: array of TLanguageList; Implode_Glue1, Implode_Glue2: String; public MaxLines: integer; constructor Create; procedure LoadList; procedure ChangeLanguage(Language: String); //procedure LoadKeys; function SetHelpID(ID: String):boolean; function GetHelpID(): string; function GetHelpStr(): TTextResult; procedure SetScrollPos(pos: double); function GetScrollPos(): double; end; var Help: THelp; implementation uses UFiles, SDL, Classes, ULanguage; //---------- //Create - Construct Class then LoadList + Standard Language + Set Standard Implode Glues //---------- constructor THelp.Create; var I, J, K: Integer; begin LoadList; MaxLines := 0; //LoadKeys; //Set Implode Glues for Backward Compatibility Implode_Glue1 := ', '; Implode_Glue2 := ' and '; if (Length(List) = 0) then //No Language Files Loaded -> Abort Loading Log.CriticalError('Could not load any Language File (Help System)'); //Standard Language (If a Language File is Incomplete) //Then use English Language for I := 0 to high(List) do //Search for English Language begin //English Language Found -> Load if Uppercase(List[I].Name) = 'ENGLISH' then begin ChangeLanguage('English'); SetLength(SEntry, Length(Entry)); for J := low(Entry) to high(Entry) do begin SetLength(SEntry[J].Sections, Length(Entry[J].Sections)); SEntry[J].ID := Entry[J].ID; SEntry[J].Title := Entry[J].Title; SEntry[J].Description := Entry[J].Description; for K := low(Entry[J].Sections) to high(Entry[J].Sections) do SEntry[J].Sections[K] := Entry[J].Sections[K]; end; SetLength(List, 0); Break; end; if (I = high(List)) then Log.LogError('English Languagefile missing! No standard Translation loaded (Help System)'); end; actualID := ''; //Standard Language END end; //---------- //LoadList - Parse the Help Dir searching Translations //---------- procedure THelp.LoadList; var SR: TSearchRec; // for parsing directory begin SetLength(List, 0); if FindFirst(LanguagesPath + '*.ini', 0, SR) = 0 then begin repeat SetLength(List, Length(List)+1); SR.Name := ChangeFileExt(SR.Name, ''); List[High(List)].Name := SR.Name; until FindNext(SR) <> 0; SysUtils.FindClose(SR); end; // if FindFirst end; //---------- //ChangeLanguage - Load the specified LanguageFile //---------- procedure THelp.ChangeLanguage(Language: String); var IniFile: TIniFile; E: integer; // entry S: TStringList; SL : TStringList; num: Integer; I, J: Integer; Keys: array of TKeys; tempStr: string; ActSection: integer; ActSub: integer; ActKey: integer; ActSubEntry: integer; function GetIDStr(ID: integer):string; var S: string; begin S := IntToStr(ID); while (Length(S)<3) do S := '0' + S; Result := 'ID_' + S; end; function GetKeyTranslation(Key: string):string; var I: Integer; begin Result := 'Error'; for I := 0 to Length(Keys) - 1 do begin if Keys[I].Key=Key then begin Result := Keys[I].Translation; break; end; end; end; begin IniFile := TIniFile.Create(LanguagesPath + Language + '.ini'); SetLength(Keys, 0); //read keys S := TStringList.Create; IniFile.ReadSectionValues('Keymap', S); SetLength(Keys, S.Count); for E := 0 to high(Keys) do begin if S.Names[E] = 'IMPLODE_GLUE1' then Implode_Glue1 := S.ValueFromIndex[E]+ ' ' else if S.Names[E] = 'IMPLODE_GLUE2' then Implode_Glue2 := ' ' + S.ValueFromIndex[E] + ' '; Keys[E].Key := S.Names[E]; Keys[E].Translation := S.ValueFromIndex[E]; end; SetLength(Entry, 0); num := IniFile.ReadInteger('config', 'NumIDs', 0); if num>0 then begin SetLength(Entry, num); for I:=1 to num do begin Entry[I-1].ID:= GetIDStr(I); Entry[I-1].Title := IniFile.ReadString(GetIDStr(I), 'Title', 'error title: ' + GetIDStr(I)); Entry[I-1].Description := IniFile.ReadString(GetIDStr(I), 'Description', 'error description: ' + GetIDStr(I)); //read subs, sections and keymapping S := TStringList.Create; IniFile.ReadSectionValues(GetIDStr(I), S); ActSub := -1; ActSubEntry := -1; ActSection := -1; ActKey := -1; //SetLength(Entry[I-1].Keys, S.Count-2); for E := 0 to S.Count-1 do begin if S.Names[E] = 'IMPLODE_GLUE1' then Implode_Glue1 := S.ValueFromIndex[E]+ ' ' else if S.Names[E] = 'IMPLODE_GLUE2' then Implode_Glue2 := ' ' + S.ValueFromIndex[E] + ' '; if (S.Names[E] <> 'Title') and (S.Names[E] <> 'Description') then begin tempStr := S.Names[E]; SL:=TStringList.Create; try ExtractStrings(['_'], [], PChar(tempStr), SL); //check new Sub if SL[0]='SUB' then begin inc(ActSub); ActSubEntry := -1; SetLength(Entry[I-1].Subs, ActSub+1); Entry[I-1].Subs[ActSub].title := S.ValueFromIndex[E]; end //check sub entry else if SL[0]='ENT' then begin inc(ActSubEntry); SetLength(Entry[I-1].Subs[ActSub].text, ActSubEntry+1); Entry[I-1].Subs[ActSub].text[ActSubEntry] := S.ValueFromIndex[E]; end //check new section else if SL[0]='SEC' then begin inc(ActSection); ActKey := -1; SetLength(Entry[I-1].Sections, ActSection+1); Entry[I-1].Sections[ActSection].name := S.ValueFromIndex[E]; end //read keymap else if ActSection=-1 then begin Log.LogError('No section in keymapping of ' + GetIDStr(I) + ' in ' + language + '.ini'); end else begin inc(ActKey); SetLength(Entry[I-1].Sections[ActSection].Keys, ActKey +1); Entry[I-1].Sections[ActSection].Keys[ActKey].Text := S.ValueFromIndex[E]; SetLength(Entry[I-1].Sections[ActSection].Keys[ActKey].Key, SL.Count); for J := 0 to SL.Count-1 do Entry[I-1].Sections[ActSection].Keys[ActKey].Key[J] := GetKeyTranslation(SL[J]); end; Finally SL.Free; end; end; end; S.Free; end; end; IniFile.Free; end; function THelp.SetHelpID(ID: String):boolean; var E: integer; // entry begin Result := false; ScrollPos := 0.0; for E := 0 to high(Entry) do begin if ID = Entry[E].ID then begin Result := true; AEntry := Entry[E]; actualID := ID; exit; end; end; //Standard Language (If a Language File is Incomplete) //Then use Standard Language for E := low(SEntry) to high(SEntry) do begin if ID = SEntry[E].ID then begin Result := true; AEntry := SEntry[E]; actualID := ID; exit; end; end; //Standard Language END end; function THelp.GetHelpID(): string; begin Result := actualID; end; function THelp.GetHelpStr(): TTextResult; var K, I, J: Integer; begin SetLength(Result.Sections, Length(AEntry.Sections)); for K := 0 to Length(AEntry.Sections) - 1 do begin SetLength(Result.Sections[K].Keys, Length(AEntry.Sections[K].Keys)); SetLength(Result.Sections[K].KeyDescription, Length(AEntry.Sections[K].Keys)); Result.Sections[K].name := AEntry.Sections[K].name; for I := 0 to Length(AEntry.Sections[K].Keys)-1 do begin SetLength(Result.Sections[K].Keys[I].Key, Length(AEntry.Sections[K].Keys[I].Key)); for J := 0 to Length(AEntry.Sections[K].Keys[I].Key) - 1 do begin Result.Sections[K].Keys[I].Key[J] := '[' + AEntry.Sections[K].Keys[I].Key[J] + ']'; end; Result.Sections[K].KeyDescription[I] := AEntry.Sections[K].Keys[I].Text; end; end; SetLength(Result.Subs, Length(AEntry.Subs)); for K := 0 to Length(AEntry.Subs) - 1 do begin Result.Subs[K].title := AEntry.Subs[K].title; Result.Subs[K].text := AEntry.Subs[K].text; end; Result.Title := AEntry.Title; Result.Description := AEntry.Description; end; procedure THelp.SetScrollPos(pos: double); begin ScrollPos := pos; end; function THelp.GetScrollPos(): double; begin Result := ScrollPos; end; end.