Browse Source

* new bunch of Gabor's changes

pierre 25 years ago
parent
commit
0b75c6e4fd
7 changed files with 1296 additions and 119 deletions
  1. 12 0
      ide/text/fixes.txt
  2. 6 2
      ide/text/fpconst.pas
  3. 33 2
      ide/text/fphelp.pas
  4. 65 11
      ide/text/whelp.pas
  5. 191 103
      ide/text/wnghelp.pas
  6. 66 1
      ide/text/wutils.pas
  7. 923 0
      ide/text/wwinhelp.pas

+ 12 - 0
ide/text/fixes.txt

@@ -1,3 +1,15 @@
+Gabors's log to 26/6/2000 commits
+========================= Already fixed ================================
+
+ [*] topics with the same name are now all shown on the Index page
+
+========================== Other improvements ============================
+
+ [+] added support for "see also" entries of Norton Guide help topics
+ [+] coloring of NG help is now integrated with the common color managment
+     system of FV
+ [+] started implementing help support for Windows Help files
+
 Gabors's log to 22/6/2000 commits
 ========================== Other improvements ============================
 

+ 6 - 2
ide/text/fpconst.pas

@@ -51,7 +51,8 @@ const
      HTMLExt              = '.htm';
      TemplateExt          = '.pt';
      NGExt                = '.ng';
-     HelpFileExts         = '*.tph;*.htm*;*'+HTMLIndexExt+';*'+NGExt;
+     WinHelpExt           = '.hlp';
+     HelpFileExts         = '*.tph;*.htm*;*'+HTMLIndexExt+';*'+NGExt+';*'+WinHelpExt;
 
      EnterSign            = #17#196#217;
 
@@ -406,7 +407,10 @@ implementation
 END.
 {
   $Log$
-  Revision 1.41  2000-06-22 09:07:11  pierre
+  Revision 1.42  2000-06-26 07:29:22  pierre
+   * new bunch of Gabor's changes
+
+  Revision 1.41  2000/06/22 09:07:11  pierre
    * Gabor changes: see fixes.txt
 
   Revision 1.40  2000/06/16 08:50:40  pierre

+ 33 - 2
ide/text/fphelp.pas

@@ -66,7 +66,7 @@ const
 implementation
 
 uses Objects,Views,App,MsgBox,Commands,
-     WUtils,WHTMLHlp,
+     WUtils,WHTMLHlp,WNGHelp,
      FPString,FPConst,FPVars,FPUtils;
 
 const
@@ -290,6 +290,14 @@ procedure InitHelpSystem;
     {$IFDEF DEBUG}SetStatus(msg_LoadingHelpFile);{$ENDIF}
   end;
 
+  procedure AddWinHelpFile(HelpFile: string);
+  begin
+    {$IFDEF DEBUG}SetStatus(msg_LoadingHelpFile+' ('+SmartPath(HelpFile)+')');{$ENDIF}
+    if HelpFacility^.AddWinHelpFile(HelpFile)=false then
+      ErrorBox(FormatStrStr(msg_failedtoloadhelpfile,HelpFile),nil);
+    {$IFDEF DEBUG}SetStatus(msg_LoadingHelpFile);{$ENDIF}
+  end;
+
   procedure AddHTMLIndexFile(HelpFile: string);
   begin
     {$IFDEF DEBUG}SetStatus(msg_LoadingHelpFile+' ('+SmartPath(HelpFile)+')');{$ENDIF}
@@ -317,6 +325,8 @@ begin
           AddHTMLIndexFile(S) else
       if UpcaseStr(ExtOf(S))=UpcaseStr(NGExt) then
           AddNGFile(S) else
+      if UpcaseStr(ExtOf(S))=UpcaseStr(WinHelpExt) then
+          AddWinHelpFile(S) else
         AddOAFile(S);
     end;
   PopStatus;
@@ -461,9 +471,27 @@ begin
   FPHTMLGetSectionColor:=OK;
 end;
 
+function FPNGGetAttrColor(Attr: char; var Color: byte): boolean;
+var OK: boolean;
+begin
+  OK:=false;
+  case Attr of
+    'A' : OK:=FPHTMLGetSectionColor(hsHeading1,Color);
+    'B' : OK:=FPHTMLGetSectionColor(hsHeading2,Color);
+    'b' : OK:=FPHTMLGetSectionColor(hsHeading5,Color);
+    'U' : OK:=FPHTMLGetSectionColor(hsHeading3,Color);
+    'N' : OK:=FPHTMLGetSectionColor(hsHeading4,Color);
+  {$ifdef DEBUGMSG}
+  else ErrorBox('Unknown attr encountered : "'+Attr+'"',nil);
+  {$endif}
+  end;
+  FPNGGetAttrColor:=OK;
+end;
+
 procedure InitHelpFiles;
 begin
   HTMLGetSectionColor:={$ifdef FPC}@{$endif}FPHTMLGetSectionColor;
+  NGGetAttrColor:={$ifdef FPC}@{$endif}FPNGGetAttrColor;
   New(HelpFiles, Init(10,10));
 end;
 
@@ -490,7 +518,10 @@ end;
 END.
 {
   $Log$
-  Revision 1.34  2000-06-22 09:07:12  pierre
+  Revision 1.35  2000-06-26 07:29:23  pierre
+   * new bunch of Gabor's changes
+
+  Revision 1.34  2000/06/22 09:07:12  pierre
    * Gabor changes: see fixes.txt
 
   Revision 1.33  2000/06/16 08:50:40  pierre

+ 65 - 11
ide/text/whelp.pas

@@ -156,7 +156,7 @@ type
       end;
 
       PKeywordDescriptors = ^TKeywordDescriptors;
-      TKeywordDescriptors = array[0..10900] of TKeywordDescriptor;
+      TKeywordDescriptors = array[0..MaxBytes div sizeof(TKeywordDescriptor)-1] of TKeywordDescriptor;
 
       PTopic = ^TTopic;
       TTopic = object
@@ -190,11 +190,17 @@ type
         function   Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
       end;
 
+      PUnsortedIndexEntryCollection = ^TUnsortedIndexEntryCollection;
+      TUnsortedIndexEntryCollection = object(TCollection)
+        function   At(Index: Sw_Integer): PIndexEntry;
+        procedure  FreeItem(Item: Pointer); virtual;
+      end;
+
       PHelpFile = ^THelpFile;
       THelpFile = object(TObject)
         ID           : word;
         Topics       : PTopicCollection;
-        IndexEntries : PIndexEntryCollection;
+        IndexEntries : PUnsortedIndexEntryCollection;
         constructor Init(AID: word);
         function    LoadTopic(HelpCtx: THelpCtx): PTopic; virtual;
         procedure   AddTopic(HelpCtx: THelpCtx; Pos: longint; const Param: string);
@@ -244,6 +250,7 @@ type
         function    AddOAHelpFile(const FileName: string): boolean;
         function    AddHTMLHelpFile(const FileName, TOCEntry: string): boolean;
         function    AddNGHelpFile(const FileName: string): boolean;
+        function    AddWinHelpFile(const FileName: string): boolean;
         function    AddHTMLIndexHelpFile(const FileName: string): boolean;
         function    LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic; virtual;
         function    TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean; virtual;
@@ -279,7 +286,7 @@ uses
 {$ifdef Linux}
   linux,
 {$endif Linux}
-  WConsts,WHTMLHlp,WNGHelp;
+  WConsts,WHTMLHlp,WNGHelp,WWinHelp;
 
 
 Function GetDosTicks:longint; { returns ticks at 18.2 Hz, just like DOS }
@@ -364,7 +371,7 @@ begin
 end;
 
 procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
-var Size,CurPtr,I: sw_word;
+var Size,CurPtr,I,MSize: sw_word;
     S: string;
 begin
   CurPtr:=0;
@@ -379,8 +386,12 @@ begin
   CurPtr:=0;
   for I:=0 to Lines^.Count-1 do
   begin
-    S:=GetStr(Lines^.At(I)); Size:=length(S);
-    Move(S[1],PByteArray(T^.Text)^[CurPtr],Size);
+    S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
+    if CurPtr+Size>=T^.TextSize then
+      MSize:=T^.TextSize-CurPtr;
+    Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
+    if MSize<>Size then
+      Break;
     Inc(CurPtr,Size);
     PByteArray(T^.Text)^[CurPtr]:=ord(hscLineBreak);
     Inc(CurPtr);
@@ -392,7 +403,7 @@ procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
 var NewSize: word;
     NewPtr: pointer;
 begin
-  NewSize:=(T^.LinkCount+1)*sizeof(T^.Links^[0]);
+  NewSize:=longint(T^.LinkCount+1)*sizeof(T^.Links^[0]);
   GetMem(NewPtr,NewSize);
   if Assigned(T^.Links) then
   begin
@@ -488,6 +499,16 @@ begin
   if Item<>nil then DisposeIndexEntry(Item);
 end;
 
+function TUnsortedIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
+begin
+  At:=inherited At(Index);
+end;
+
+procedure TUnsortedIndexEntryCollection.FreeItem(Item: Pointer);
+begin
+  if Item<>nil then DisposeIndexEntry(Item);
+end;
+
 function TIndexEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
 var K1: PIndexEntry absolute Key1;
     K2: PIndexEntry absolute Key2;
@@ -497,6 +518,8 @@ begin
   S1:=UpcaseStr(K1^.Tag^); S2:=UpcaseStr(K2^.Tag^);
   if S1<S2 then R:=-1 else
   if S1>S2 then R:=1 else
+  if K1^.FileID<K2^.FileID then R:=-1 else
+  if K1^.FileID>K2^.FileID then R:= 1 else
   R:=0;
   Compare:=R;
 end;
@@ -1006,6 +1029,13 @@ begin
   AddNGHelpFile:=AddFile(H);;
 end;
 
+function THelpFacility.AddWinHelpFile(const FileName: string): boolean;
+var H: PHelpFile;
+begin
+  H:=New(PWinHelpFile, Init(FileName, LastID+1));
+  AddWinHelpFile:=AddFile(H);;
+end;
+
 function THelpFacility.AddHTMLIndexHelpFile(const FileName: string): boolean;
 var H: PHelpFile;
 begin
@@ -1112,12 +1142,32 @@ begin
   LastFirstChar:=FirstChar;
   NLFlag:=0;
 end;
+function FormatAlias(Alias: string): string;
+var StartP,EndP: sw_integer;
+begin
+  repeat
+    StartP:=Pos('  ',Alias);
+    if StartP>0 then
+    begin
+      EndP:=StartP;
+      while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
+      Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
+    end;
+  until StartP=0;
+  if Assigned(HelpFacility) then
+    if length(Alias)>IndexTabSize-4 then
+      Alias:=Trim(copy(Alias,1,IndexTabSize-4-2))+'..';
+  FormatAlias:=Alias;
+end;
 procedure AddKeyword(KWS: string);
 begin
   Inc(KWCount); if KWCount=1 then NLFlag:=0;
   if (KWCount=1) or
      ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
      NewSection(Upcase(KWS[1]));
+
+  KWS:=FormatAlias(KWS);
+
   if (NLFlag mod 2)=0
      then Line:=' '+#2+KWS+#2
      else begin
@@ -1141,14 +1191,15 @@ begin
   begin
     AddLine(' '+msg_helpindex);
     KWCount:=0; Line:='';
-    T^.LinkCount:=Keywords^.Count;
+    T^.LinkCount:=Min(Keywords^.Count,MaxBytes div sizeof(T^.Links^[0])-1);
     GetMem(T^.Links,T^.LinkSize);
 
-    for I:=0 to Keywords^.Count-1 do
+    for I:=0 to T^.LinkCount-1 do
     begin
       KW:=Keywords^.At(I);
       AddKeyword(KW^.Tag^);
-      T^.Links^[I].Context:=longint(KW^.HelpCtx); T^.Links^[I].FileID:=KW^.FileID;
+      T^.Links^[I].Context:=longint(KW^.HelpCtx);
+      T^.Links^[I].FileID:=KW^.FileID;
     end;
     FlushLine;
     AddLine('');
@@ -1185,7 +1236,10 @@ end;
 END.
 {
   $Log$
-  Revision 1.24  2000-06-22 09:07:14  pierre
+  Revision 1.25  2000-06-26 07:29:23  pierre
+   * new bunch of Gabor's changes
+
+  Revision 1.24  2000/06/22 09:07:14  pierre
    * Gabor changes: see fixes.txt
 
   Revision 1.23  2000/06/16 08:50:44  pierre

+ 191 - 103
ide/text/wnghelp.pas

@@ -75,6 +75,7 @@ type
         { array of TNGSeeAlsoRec }
       end;
 
+      PNGSeeAlsoRec = ^TNGSeeAlsoRec;
       TNGSeeAlsoRec = packed record
         EntryCount    : word;
         Entries       : record end;
@@ -89,6 +90,12 @@ type
         Container: PNGContainerRecord;
       end;
 
+      PLinkRec = ^TLinkRec;
+      TLinkRec = record
+        Name     : string;
+        FilePos  : longint;
+      end;
+
       PNGHelpFile = ^TNGHelpFile;
       TNGHelpFile = object(THelpFile)
         constructor Init(AFileName: string; AID: word);
@@ -99,17 +106,30 @@ type
       private
         F: PStream;
         Header: TNGFileHeader;
+        FirstRecordPos: longint;
+        IndexLoaded: boolean;
 {        NextHelpCtx: longint;}
         function ReadHeader: boolean;
         function ReadContainer(EnumProc: pointer): boolean;
-        function ReadTopicRec(Lines: PUnsortedStringCollection): boolean;
+        function ReadTopicRec(LineEnumProc: pointer; LinkEnumProc: pointer): boolean;
         function ReadRecord(var R: TRecord; ReadData: boolean): boolean;
       end;
 
+    TNGGetAttrColorProc = function(Attr: char; var Color: byte): boolean;
+
+function DefNGGetAttrColor(Attr: char; var Color: byte): boolean;
+
+const NGGetAttrColor : TNGGetAttrColorProc = DefNGGetAttrColor;
+
 implementation
 
 uses CallSpec;
 
+function DefNGGetAttrColor(Attr: char; var Color: byte): boolean;
+begin
+  DefNGGetAttrColor:=false;
+end;
+
 function NGDecompressStr(const S: string): string;
 var NS: string;
     I: sw_integer;
@@ -134,6 +154,7 @@ function TranslateStr(const S: string): string;
 var NS: string;
     I: sw_integer;
     InHiLite: boolean;
+    Color: byte;
 begin
   NS:=''; InHiLite:=false;
   I:=1;
@@ -144,25 +165,14 @@ begin
                Inc(I);
                case S[I] of
                  '^' : NS:=NS+'^';
-                 'B' : begin
-                         if InHiLite then
-                           NS:=NS+hscNormText
-                         else
-                           NS:=NS+hscTextColor+chr(15);
-                         InHiLite:=not InHiLite;
-                       end;
-                 'b' : begin
+                 'A'..'Z',
+                 'a'..'z' :
+                       begin
                          if InHiLite then
                            NS:=NS+hscNormText
                          else
-                           NS:=NS+hscTextColor+chr(11);
-                         InHiLite:=not InHiLite;
-                       end;
-                 'U' : begin
-                         if InHiLite then
-                           NS:=NS+hscNormText
-                         else
-                           NS:=NS+hscTextColor+chr(3);
+                           if NGGetAttrColor(S[I],Color) then
+                             NS:=NS+hscTextColor+chr(Color);
                          InHiLite:=not InHiLite;
                        end;
                else
@@ -190,40 +200,7 @@ begin
 end;
 
 constructor TNGHelpFile.Init(AFileName: string; AID: word);
-function FormatAlias(Alias: string): string;
-var StartP,EndP: sw_integer;
-begin
-  repeat
-    StartP:=Pos('  ',Alias);
-    if StartP>0 then
-    begin
-      EndP:=StartP;
-      while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
-      Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
-    end;
-  until StartP=0;
-  if Assigned(HelpFacility) then
-    if length(Alias)>HelpFacility^.IndexTabSize-4 then
-      Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
-  FormatAlias:=Alias;
-end;
-procedure AddToIndex(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
-var S: string;
-begin
-  S:=Trim(P^.Name);
-  S:=TranslateStr(S);
-  S:=Trim(FormatAlias(S));
-  if (S<>'') and (P^.FilePos<>-1) then
-    begin
-{      Inc(NextHelpCtx);}
-      AddIndexEntry(S,P^.FilePos);
-      AddTopic(P^.FilePos,P^.FilePos,'');
-    end;
-end;
 var OK: boolean;
-    FS: longint;
-    R: TRecord;
-    L: longint;
 begin
   if inherited Init(AID)=false then Fail;
   F:=New(PBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
@@ -231,38 +208,10 @@ begin
   if OK then OK:=(F^.Status=stOK);
   if OK then
     begin
-      FS:=F^.GetSize;
       OK:=ReadHeader;
+      if OK then
+        FirstRecordPos:=F^.GetPos;
     end;
-  while OK do
-  begin
-    L:=F^.GetPos;
-    if (L>=FS) then Break;
-    OK:=ReadRecord(R,false);
-    if (OK=false) then Break;
-    case R.SClass of
-      ng_rtContainer : begin F^.Seek(L); OK:=ReadContainer(@AddToIndex); end;
-{      ng_rtTopic     : begin F^.Seek(L); OK:=ReadTopicRec; end;}
-    else
-     begin
-     {$ifdef DEBUGMSG}
-       ClearFormatParams;
-       AddFormatParamInt(R.SClass);
-       AddFormatParamInt(L);
-       AddFormatParamInt(R.Size);
-       ErrorBox('Uknown help record tag %x encountered, '+
-                'offset %x, size %d',@FormatParams);
-     {$else}
-       {Skip};
-     {$endif}
-     end;
-    end;
-    if OK then
-       begin
-         Inc(L, sizeof(TNGRecordHeader)+R.Size); F^.Seek(L);
-         OK:=(F^.Status=stOK);
-       end;
-  end;
   if OK=false then
   begin
     Done;
@@ -279,22 +228,10 @@ begin
   ReadHeader:=OK;
 end;
 
-function KillSpecChars(const S: string): string;
-var I: sw_integer;
-    RS: string;
-begin
-  RS:='';
-  for I:=1 to length(S) do
-    if S[I]>=#32 then
-      RS:=RS+S[I];
-  KillSpecChars:=RS;
-end;
-
 function TNGHelpFile.ReadContainer(EnumProc: pointer): boolean;
 var OK: boolean;
     R: TRecord;
-    I,L: longint;
-    CI: TNGContainerItem;
+    I: longint;
     P: pointer;
     CIR: TContainerItemRec;
 begin
@@ -322,23 +259,45 @@ begin
   ReadContainer:=OK;
 end;
 
-function TNGHelpFile.ReadTopicRec(Lines: PUnsortedStringCollection): boolean;
+function TNGHelpFile.ReadTopicRec(LineEnumProc, LinkEnumProc: pointer): boolean;
 var OK: boolean;
     R: TRecord;
     I: sw_integer;
     LineP: pointer;
     S: string;
+    ParamS: string;
+    NextLinkOfsPtr,NextLinkNamePtr: pointer;
+    SeeAlso: PNGSeeAlsoRec;
+    LR: TLinkRec;
 begin
   OK:=ReadRecord(R, true);
   if OK then
   with TNGTopicRecord(R.Data^) do
   begin
     LineP:=@TopicLines;
+    if Assigned(LineEnumProc) then
     for I:=1 to NumberOfLines do
     begin
       S:=StrPas(LineP);
-      Lines^.InsertStr(NGDecompressStr(S));
-      LineP:=pointer(longint(LineP)+length(S)+1);
+      ParamS:=NGDecompressStr(S);
+      CallPointerLocal(LineEnumProc,PreviousFramePointer,@ParamS);
+      Inc(longint(LineP),length(S)+1);
+    end;
+    if Assigned(LinkEnumProc) and (SeeAlsoOfs>0) then
+    begin
+      SeeAlso:=@PByteArray(R.Data)^[NGMinRecordSize-sizeof(TNGRecordHeader)+SeeAlsoOfs];
+      NextLinkOfsPtr:=@SeeAlso^.Entries;
+      NextLinkNamePtr:=@PByteArray(NextLinkOfsPtr)^[SeeAlso^.EntryCount*4];
+      for I:=1 to SeeAlso^.EntryCount do
+      begin
+        FillChar(LR,sizeof(LR),0);
+        S:=StrPas(NextLinkNamePtr);
+        LR.Name:=S;
+        Move(NextLinkOfsPtr^,LR.FilePos,4);
+        CallPointerLocal(LinkEnumProc,PreviousFramePointer,@LR);
+        Inc(longint(NextLinkNamePtr),length(S)+1);
+        Inc(longint(NextLinkOfsPtr),4);
+      end;
     end;
   end;
   DisposeRecord(R);
@@ -374,20 +333,142 @@ begin
 end;
 
 function TNGHelpFile.LoadIndex: boolean;
+{function FormatAlias(Alias: string): string;
+var StartP,EndP: sw_integer;
+begin
+  repeat
+    StartP:=Pos('  ',Alias);
+    if StartP>0 then
+    begin
+      EndP:=StartP;
+      while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
+      Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
+    end;
+  until StartP=0;
+  if Assigned(HelpFacility) then
+    if length(Alias)>HelpFacility^.IndexTabSize-4 then
+      Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
+  FormatAlias:=Alias;
+end;}
+procedure AddToIndex(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
+var S: string;
+begin
+  S:=Trim(P^.Name);
+  S:=TranslateStr(S);
+  S:=Trim({FormatAlias}(S));
+  if (S<>'') and (P^.FilePos<>-1) then
+    begin
+{      Inc(NextHelpCtx);}
+      AddIndexEntry(S,P^.FilePos);
+      AddTopic(P^.FilePos,P^.FilePos,'');
+    end;
+end;
+var OK: boolean;
+    FS: longint;
+    R: TRecord;
+    L: longint;
 begin
-  LoadIndex:=false;
+  if IndexLoaded then OK:=true else
+  begin
+    FS:=F^.GetSize;
+    OK:=FirstRecordPos<>0;
+
+    while OK do
+    begin
+      L:=F^.GetPos;
+      if (L>=FS) then Break;
+      OK:=ReadRecord(R,false);
+      if (OK=false) then Break;
+      case R.SClass of
+        ng_rtContainer : begin F^.Seek(L); OK:=ReadContainer(@AddToIndex); end;
+        ng_rtTopic     : ;
+      else
+       begin
+       {$ifdef DEBUGMSG}
+         ClearFormatParams;
+         AddFormatParamInt(R.SClass);
+         AddFormatParamInt(L);
+         AddFormatParamInt(R.Size);
+         ErrorBox('Uknown help record tag %x encountered, '+
+                  'offset %x, size %d',@FormatParams);
+       {$else}
+         {Skip};
+       {$endif}
+       end;
+      end;
+      if OK then
+         begin
+           Inc(L, sizeof(TNGRecordHeader)+R.Size); F^.Seek(L);
+           OK:=(F^.Status=stOK);
+         end;
+    end;
+    IndexLoaded:=OK;
+  end;
+  LoadIndex:=OK;
 end;
 
 function TNGHelpFile.ReadTopic(T: PTopic): boolean;
+function ExtractStr(var Buf; BufSize: word): string;
+var S: string;
+    I,Size: integer;
+    StartP,EndP: sw_integer;
+begin
+  S:='';
+  Size:=BufSize;
+  while (PByteArray(@Buf)^[Size-1]=0) and (Size>0) do
+    Dec(Size);
+  S:=MemToStr(Buf,Size);
+
+  S:=NGDecompressStr(S);
+
+  for I:=1 to length(S) do
+    if S[I]=#0 then S[I]:=#32;
+
+  { kill long spaces }
+  repeat
+    StartP:=Pos('  ',S);
+    if StartP>0 then
+    begin
+      EndP:=StartP;
+      while (EndP+1<=length(S)) and (S[EndP+1]=' ') do Inc(EndP);
+      S:=copy(S,1,StartP-1)+hscLineBreak+copy(S,EndP+1,High(S));
+    end;
+  until StartP=0;
+
+  S:=Trim(S);
+
+  ExtractStr:=S;
+end;
 var Lines: PUnsortedStringCollection;
+procedure AddLine(const S: string);
+begin
+  Lines^.InsertStr(S);
+end;
 procedure AddToTopic(P: PContainerItemRec); {$ifndef FPC}far;{$endif}
 begin
-  Lines^.InsertStr(hscLink+P^.Name+hscLink);
+  AddLine(hscLink+Trim(P^.Name)+hscLink);
+  AddLinkToTopic(T,ID,P^.FilePos);
+end;
+procedure AddTopicLine(P: PString); {$ifndef FPC}far;{$endif}
+begin
+  AddLine(' '+GetStr(P));
+end;
+var LinkCount: sw_integer;
+procedure AddLink(P: PLinkRec); {$ifndef FPC}far;{$endif}
+begin
+  Inc(LinkCount);
+  if LinkCount=1 then
+  begin
+    AddLine('');
+    AddLine(' See also :');
+  end;
+  AddLine('  '+hscLink+Trim(P^.Name)+hscLink);
   AddLinkToTopic(T,ID,P^.FilePos);
 end;
 var OK: boolean;
     R: TRecord;
 begin
+  LinkCount:=0;
   New(Lines, Init(100,100));
   F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
   if OK then OK:=ReadRecord(R,false);
@@ -395,17 +476,21 @@ begin
       ng_rtContainer :
         begin
           F^.Seek(T^.FileOfs);
-          Lines^.InsertStr(' ');
+          AddLine('');
           OK:=ReadContainer(@AddToTopic);
           RenderTopic(Lines,T);
         end;
       ng_rtTopic     :
         begin
           F^.Seek(T^.FileOfs);
-          Lines^.InsertStr(' ');
-          OK:=ReadTopicRec(Lines);
+          AddLine('');
+          OK:=ReadTopicRec(@AddTopicLine,@AddLink);
           TranslateLines(Lines);
-          Lines^.InsertStr(' ');
+          AddLine('');
+          { include copyright info }
+{          AddLine(CharStr('Ä',80));
+          AddLine(ExtractStr(Header.GuideName,sizeof(Header.GuideName)));
+          AddLine(ExtractStr(Header.Credits,sizeof(Header.Credits)));}
           RenderTopic(Lines,T);
         end;
   else OK:=false;
@@ -423,7 +508,10 @@ end;
 END.
 {
   $Log$
-  Revision 1.1  2000-06-22 09:07:15  pierre
+  Revision 1.2  2000-06-26 07:29:23  pierre
+   * new bunch of Gabor's changes
+
+  Revision 1.1  2000/06/22 09:07:15  pierre
    * Gabor changes: see fixes.txt
 
 }

+ 66 - 1
ide/text/wutils.pas

@@ -66,12 +66,27 @@ type
     S       : PStream;
   end;
 
+  PFastBufStream = ^TFastBufStream;
+  TFastBufStream = object(TBufStream)
+    procedure   Seek(Pos: Longint); virtual;
+  private
+    BasePos: longint;
+  end;
+
   PTextCollection = ^TTextCollection;
   TTextCollection = object(TStringCollection)
     function LookUp(const S: string; var Idx: sw_integer): string;
     function Compare(Key1, Key2: Pointer): sw_Integer; virtual;
   end;
 
+  PIntCollection = ^TIntCollection;
+  TIntCollection = object(TSortedCollection)
+    function  Compare(Key1, Key2: Pointer): sw_Integer; virtual;
+    procedure FreeItem(Item: Pointer); virtual;
+    procedure Add(Item: longint);
+    function  Contains(Item: longint): boolean;
+  end;
+
 {$ifdef TPUNIXLF}
   procedure readln(var t:text;var s:string);
 {$endif}
@@ -609,6 +624,33 @@ begin
   S.WriteStr(Item);
 end;
 
+function TIntCollection.Contains(Item: longint): boolean;
+var Index: sw_integer;
+begin
+  Contains:=Search(pointer(Item),Index);
+end;
+
+procedure TIntCollection.Add(Item: longint);
+begin
+  Insert(pointer(Item));
+end;
+
+function TIntCollection.Compare(Key1, Key2: Pointer): sw_Integer;
+var K1: longint absolute Key1;
+    K2: longint absolute Key2;
+    R: integer;
+begin
+  if K1<K2 then R:=-1 else
+  if K1>K2 then R:= 1 else
+  R:=0;
+  Compare:=R;
+end;
+
+procedure TIntCollection.FreeItem(Item: Pointer);
+begin
+  { do nothing here }
+end;
+
 constructor TNulStream.Init;
 begin
   inherited Init;
@@ -684,6 +726,26 @@ begin
   S^.Write(Buf,Count);
 end;
 
+procedure TFastBufStream.Seek(Pos: Longint);
+function BufStartPos: longint;
+begin
+  BufStartPos:=Position-BufPtr;
+end;
+var RelOfs: longint;
+begin
+  RelOfs:=Pos-{BufStartPos}BasePos;
+  if (RelOfs<0) or (RelOfs>=BufEnd) or (BufEnd=0) then
+    begin
+      inherited Seek(Pos);
+      BasePos:=Pos;
+    end
+  else
+    begin
+      BufPtr:=RelOfs;
+      Position:=Pos;
+    end;
+end;
+
 function TTextCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
 var K1: PString absolute Key1;
     K2: PString absolute Key2;
@@ -972,7 +1034,10 @@ end;
 END.
 {
   $Log$
-  Revision 1.25  2000-06-22 09:07:15  pierre
+  Revision 1.26  2000-06-26 07:29:23  pierre
+   * new bunch of Gabor's changes
+
+  Revision 1.25  2000/06/22 09:07:15  pierre
    * Gabor changes: see fixes.txt
 
   Revision 1.24  2000/06/16 21:16:41  pierre

+ 923 - 0
ide/text/wwinhelp.pas

@@ -0,0 +1,923 @@
+{
+    $Id$
+    This file is part of the Free Pascal Integrated Development Environment
+    Copyright (c) 2000 by Berczi Gabor
+
+    Help support for Windows Help (.HLP) files
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+{$R-}
+unit WWinHelp;
+
+interface
+
+uses Objects,
+     WUtils,WHelp;
+
+const
+     WinHelpMagicNo             = $00035F3F;
+     WinHelpBTreeHeaderMagicNo  = $293B;
+     WinHelpSystemHeaderMagicNo = $036c;
+
+     { WinHelp Btree dataformat descriptors }
+     wh_bdf_Long           = 'L';
+     wh_bdf_ASCIIZ         = 'F';
+     wh_bdf_ASCIIZ2        = 'i';
+     wh_bdf_Short          = '2';
+     wh_bdf_Long2          = '4';
+     wh_bdf_ASCIIZ3        = 'z';
+
+     { WinHelp system record type }
+     wh_srt_Title          =  1;
+     wh_srt_Copyright      =  2;
+     wh_srt_Contents       =  3;
+     wh_srt_Config         =  4;
+     wh_srt_Icon           =  5;
+     wh_srt_Window         =  6;
+     wh_srt_Citation       =  8;
+     wh_srt_LangID         =  9;
+     wh_srt_Content        = 10;
+     wh_srt_DefFont        = 11;
+     wh_srt_FtIndex        = 12;
+     wh_srt_Groups         = 13;
+     wh_srt_KeyIndex       = 14;
+     wh_srt_Language       = 18;
+     wh_srt_DLLMaps        = 19;
+
+type
+      PInteger = ^integer;
+
+      TWinHelpHeader = packed record
+        MagicNo          : longint;
+        DirectoryStart   : longint;
+        NonDirectoryStart: longint;
+        EntireFileSize   : longint;
+      end;
+
+      TWinHelpFileEntryHeader = packed record
+        ReservedSpace    : longint;
+        UsedSpace        : longint;
+        FileFlags        : byte;
+      end;
+
+      TWinHelpRecordHeader = packed record
+        RecordType       : word;
+        DataSize         : word;
+      end;
+
+      TWinHelpBTreeHeader = packed record
+        Magic            : word;
+        Flags            : word; { 0x0002 always set, 0x0400 set if directory }
+        PageSize         : word;
+        DataFormat       : array[1..16] of char;
+        MustBeZero       : word; { $0000 }
+        PageSplits       : word;
+        RootPage         : word;
+        MustBeNegOne     : integer; { $ffff }
+        TotalPages       : word;
+        NumLevels        : word;
+        TotalEntries     : word;
+        Pages            : record end;
+      end;
+
+      TWinHelpBTreeIndexHeader = packed record
+        Unknown          : longint;
+        NumEntries       : word; { no of index-entries }
+        PrevPage         : integer;
+      end;
+
+      {
+      TWinHelpBTreeIndexEntry = packed record
+        FileName   : STRINGZ;
+        PageNumber : word;
+      end;
+      }
+
+      TWinHelpBTreeNodeHeader = packed record
+        Unknown          : longint;
+        NumEntries       : word; { no of index-entries }
+        PrevPage         : integer;
+        NextPage         : integer;
+      end;
+
+      TWinHelpSystemHeader = packed record
+        Magic            : word;
+        Version          : word;
+        Always1          : word;
+        GenDate          : longint;
+        Flags            : word;
+      end;
+
+      TWinHelpTopicBlockHeader = packed record
+        LastTopicLink    : longint;
+        FirstTopicLink   : longint;
+        LastTopicHeader  : longint;
+      end;
+
+      TWinHelpTopicBlock = packed record
+        Header           : TWinHelpTopicBlockHeader;
+        Data             : record end;
+      end;
+
+      TTopicBlock = record
+        Header       : TWinHelpTopicBlockHeader;
+        DataSize     : longint;
+        DataPtr      : pointer;
+      end;
+
+      PWinHelpFile = ^TWinHelpFile;
+      TWinHelpFile = object(THelpFile)
+        constructor Init(AFileName: string; AID: word);
+        destructor  Done; virtual;
+      public
+        function    LoadIndex: boolean; virtual;
+        function    ReadTopic(T: PTopic): boolean; virtual;
+      private
+        F: PStream;
+        Header: TWinHelpHeader;
+        SysHeader: TWinHelpSystemHeader;
+        Title: string;
+        CNTFileName: string;
+        PhrasesStart: longint;
+        TTLBTreeStart: longint;
+        TopicFileStart: longint;
+        Phrases: PUnsortedStringCollection;
+        TreeDone: boolean;
+        IndexLoaded: boolean;
+        function ReadHeader: boolean;
+        function ReadInternalDirectory: boolean;
+        function ProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word;
+                   ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
+        function ProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
+                   TotalPages: word; ReadIndexEntryMethod, ReadLeafEntryMethod: pointer;
+                   ScannedPages: PIntCollection): boolean;
+        function ProcessTree(ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; NoDuplicates: boolean): boolean;
+        function IDIRReadIndexEntry(SubPageNo: PInteger): boolean;
+        function IDIRReadLeafEntry(P: pointer): boolean;
+        function IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
+        function TTLBReadIndexEntry(SubPageNo: PInteger): boolean;
+        function TTLBReadLeafEntry(P: pointer): boolean;
+        function TTLBProcessTopicEntry(const TopicTitle: string; FileOfs: longint): boolean;
+        function ReadSystemFile: boolean;
+        function ReadPhraseFile: boolean;
+        function ReadTTLBTree: boolean;
+        function TopicBlockSize: word;
+        function LZ77Compressed: boolean;
+        procedure ExtractTopicOffset(TopicOffset: longint; var TopicBlockNo, TopicBlockOffset: word);
+        function  ReadTopicBlock(BlockNo: word; var T: TTopicBlock; ReadData: boolean): boolean;
+      end;
+
+implementation
+
+uses Crt,CallSpec;
+
+function ReadString(F: PStream): string;
+var S: string;
+    C: char;
+begin
+  S:='';
+  if Assigned(F) then
+  repeat
+    F^.Read(C,sizeof(C));
+    if (F^.Status=stOK) and (C<>#0) then
+      S:=S+C;
+  until (C=#0) or (F^.Status<>stOK);
+  ReadString:=S;
+end;
+
+constructor TWinHelpFile.Init(AFileName: string; AID: word);
+var OK: boolean;
+begin
+  if inherited Init(AID)=false then Fail;
+  New(Phrases, Init(1000,1000));
+  F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
+  OK:=F<>nil;
+  if OK then OK:=(F^.Status=stOK);
+  if OK then
+    begin
+      OK:=ReadHeader;
+    end;
+  if OK=false then
+  begin
+    Done;
+    Fail;
+  end;
+end;
+
+function TWinHelpFile.ReadHeader: boolean;
+var OK: boolean;
+begin
+  F^.Read(Header,sizeof(Header));
+  OK:=(F^.Status=stOK);
+  OK:=OK and (Header.MagicNo=WinHelpMagicNo);
+  if OK then
+  begin
+    F^.Seek(Header.DirectoryStart);
+    OK:=(F^.Status=stOK);
+  end;
+  if OK then
+    OK:=ReadInternalDirectory;
+  ReadHeader:=OK;
+end;
+
+function TWinHelpFile.TopicBlockSize: word;
+var Size: word;
+begin
+  if (SysHeader.Version<=16) then
+    Size:=2048 else
+  if (SysHeader.Flags in[0,4]) then
+    Size:=4096
+  else
+    Size:=2048;
+  TopicBlockSize:=Size;
+end;
+
+function TWinHelpFile.LZ77Compressed: boolean;
+begin
+  LZ77Compressed:=(SysHeader.Version>16) and (SysHeader.Flags<>0);
+end;
+
+function TWinHelpFile.ReadSystemFile: boolean;
+var OK: boolean;
+    FH: TWinHelpFileEntryHeader;
+    RH: TWinHelpRecordHeader;
+    StartOfs,RecStartOfs: longint;
+begin
+  F^.Read(FH,sizeof(FH));
+  OK:=(F^.Status=stOK);
+  StartOfs:=F^.GetPos;
+  F^.Read(SysHeader,sizeof(SysHeader));
+  OK:=OK and (F^.Status=stOK);
+  OK:=OK and (SysHeader.Magic=WinHelpSystemHeaderMagicNo);
+  if OK then
+  if SysHeader.Version>16 then
+  begin
+    repeat
+      F^.Read(RH,sizeof(RH));
+      OK:=(F^.Status=stOK);
+      RecStartOfs:=F^.GetPos;
+      if OK then
+      begin
+        case RH.RecordType of
+          wh_srt_Title   : Title:=ReadString(F);
+          wh_srt_Content : CNTFileName:=ReadString(F);
+        end;
+        if F^.GetPos<>RecStartOfs+RH.DataSize then
+          F^.Seek(RecStartOfs+RH.DataSize);
+        OK:=(F^.Status=stOK);
+      end;
+    until (OK=false) or (F^.GetPos>=StartOfs+FH.UsedSpace);
+  end
+ else
+  Title:=ReadString(F);
+  OK:=OK and (F^.Status=stOK);
+  ReadSystemFile:=OK;
+end;
+
+function LZ77Decompress(SrcBufP: pointer; SrcSize: longint; DestBufP: pointer; DestSize: longint): longint;
+var SrcBuf: PByteArray absolute SrcBufP;
+    DestBuf: PByteArray absolute DestBufP;
+var SrcOfs: longint;
+function GetByte: byte;
+begin
+  GetByte:=PByteArray(SrcBuf)^[SrcOfs];
+  Inc(SrcOfs);
+end;
+var DestOfs: longint;
+procedure PutByte(B: byte);
+begin
+  PByteArray(DestBuf)^[DestOfs]:=B;
+  Inc(DestOfs);
+end;
+var B,Mask: byte;
+    Len,J: word;
+    I: integer;
+const N = 4096; F=16;
+begin
+  SrcOfs:=0; DestOfs:=0; I:=N-F;
+  while (SrcOfs<SrcSize) do
+  begin
+    B:=GetByte;
+    for Mask:=0 to 7 do
+    begin
+      if SrcOfs=SrcSize then Break;
+      if (B and (1 shl Mask))<>0 then
+        begin
+          J:=GetByte;
+          Len:=GetByte;
+          J:=J+(Len and $0f) shl 8;
+          Len:=(Len and $f0) shr 4+3;
+          while (Len>0) do
+          begin
+            PutByte(PByteArray(DestBuf)^[DestOfs-J-1]);
+            {Inc(J); }Inc(I);
+            Dec(Len);
+          end;
+        end
+      else
+        begin
+          PutByte(GetByte);
+          I:=(I+1) and (N-1);
+        end;
+    end;
+  end;
+  LZ77Decompress:=DestOfs;
+end;
+
+function TWinHelpFile.ReadPhraseFile: boolean;
+var OK: boolean;
+    FH: TWinHelpFileEntryHeader;
+    NumPhrases: word;
+    DecompSize: longint;
+    W: word;
+    PhraseOfss: PWordArray;
+    PhraseOfssSize: word;
+    PhraseBuf: PByteArray;
+    TempBuf: pointer;
+    TempSize: longint;
+    I,PhraseBufSize,PhraseOfs,PhraseSize: longint;
+    S: string;
+begin
+  F^.Read(FH,sizeof(FH));
+  OK:=(F^.Status=stOK);
+  F^.Read(NumPhrases,sizeof(NumPhrases));
+  F^.Read(W,sizeof(W));
+  OK:=(F^.Status=stOK) and (W=$0100);
+  if OK then
+  begin
+    PhraseOfssSize:=(NumPhrases+1)*sizeof(word);
+    GetMem(PhraseOfss,PhraseOfssSize);
+    F^.Read(W,sizeof(W));
+    if W=2*(NumPhrases+1) then
+      begin
+        { uncompressed data }
+        PhraseOfss^[0]:=W;
+        F^.Read(PhraseOfss^[1],PhraseOfssSize-sizeof(word)*1);
+        DecompSize:=FH.UsedSpace-(PhraseOfssSize+2+2+2);
+        PhraseBufSize:=DecompSize;
+        GetMem(PhraseBuf,PhraseBufSize);
+        F^.Read(PhraseBuf^,DecompSize);
+      end
+    else
+      begin
+        DecompSize:=W;
+        F^.Read(W,sizeof(W));
+        DecompSize:=DecompSize+longint(W) shl 16;
+        Inc(DecompSize);
+        PhraseOfss^[0]:=DecompSize;
+        F^.Read(PhraseOfss^[1],PhraseOfssSize);
+        PhraseBufSize:=DecompSize+10;
+        GetMem(PhraseBuf,PhraseBufSize); FillChar(PhraseBuf^,DecompSize,0);
+        TempSize:=FH.UsedSpace-(PhraseOfssSize+2+2+2);
+        GetMem(TempBuf,TempSize);
+        F^.Read(TempBuf^,TempSize);
+        LZ77Decompress(TempBuf,TempSize,PhraseBuf,DecompSize);
+        FreeMem(TempBuf,TempSize);
+      end;
+    for I:=1 to NumPhrases do
+    begin
+      PhraseOfs:=PhraseOfss^[I]-PhraseOfss^[1];
+      if I=NumPhrases then
+        PhraseSize:=DecompSize-PhraseOfs
+      else
+        PhraseSize:=PhraseOfss^[I+1]-PhraseOfss^[I];
+      S:=MemToStr(PhraseBuf^[PhraseOfs],PhraseSize);
+      Phrases^.InsertStr(S);
+    end;
+    FreeMem(PhraseOfss,PhraseOfssSize);
+    FreeMem(PhraseBuf,PhraseBufSize);
+  end;
+  ReadPhraseFile:=OK;
+end;
+
+function TWinHelpFile.ProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word;
+          ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
+var OK: boolean;
+    BNH: TWinHelpBTreeNodeHeader;
+    Count: longint;
+    PageOfs: longint;
+    CurPage: longint;
+begin
+  CurPage:=PageNo;
+  repeat
+    PageOfs:=PagesBase+longint(PageSize)*CurPage;
+    F^.Seek(PageOfs);
+    OK:=(F^.Status=stOK);
+    if OK then
+    begin
+      F^.Read(BNH,sizeof(BNH));
+      OK:=(F^.Status=stOK);
+    end;
+    if OK then
+    if (ScannedPages<>nil) and ScannedPages^.Contains(CurPage) then
+      Break
+    else
+    begin
+      if Assigned(ScannedPages) then ScannedPages^.Add(CurPage);
+      Count:=0;
+      repeat
+        TreeDone:=not (longint(CallPointerMethod(ReadLeafEntryMethod,@Self,nil))<>0);
+        Inc(Count);
+      until (OK=false) or (F^.Status<>stOK) or TreeDone or (Count>=BNH.NumEntries){ or
+            (F^.GetPos>=PageOfs+PageSize-BNH.NumEntries)};
+    end;
+
+    if (BNH.PrevPage<>-1) and (TreeDone=false) then
+      CurPage:=BNH.PrevPage;
+  until (OK=false) or TreeDone or (BNH.PrevPage=-1);
+  ProcessLeafPage:=OK;
+end;
+
+function TWinHelpFile.ProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
+         TotalPages: word; ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; ScannedPages: PIntCollection): boolean;
+var BIH: TWinHelpBTreeIndexHeader;
+    I: integer;
+    SubPageNo: integer;
+    OK: boolean;
+    OldPos: longint;
+    CurPage: longint;
+begin
+  CurPage:=PageNo;
+  repeat
+    F^.Seek(PagesBase+longint(PageSize)*CurPage);
+    OK:=(F^.Status=stOK);
+    if OK then
+    begin
+      F^.Read(BIH,sizeof(BIH));
+      OK:=(F^.Status=stOK);
+    end;
+    if OK then
+    if (ScannedPages<>nil) and ScannedPages^.Contains(CurPage) then
+      Break
+    else
+    begin
+      if Assigned(ScannedPages) then ScannedPages^.Add(CurPage);
+      for I:=1 to BIH.NumEntries do
+      begin
+        SubPageNo:=-1;
+        OK:=(longint(CallPointerMethod(ReadIndexEntryMethod,@Self,@SubPageNo))<>0);
+        OK:=OK and (F^.Status=stOK);
+        if OK then
+          if CurLevel<MaxLevel-1 then
+            begin
+              if (0<=SubPageNo) then
+               if (ScannedPages=nil) or (ScannedPages^.Contains(SubPageNo)=false) then
+              begin
+                OldPos:=F^.GetPos;
+                OK:=ProcessIndexPage(CurLevel+1,MaxLevel,PagesBase,PageSize,SubPageNo,TotalPages,
+                  ReadIndexEntryMethod,ReadLeafEntryMethod,ScannedPages);
+                if F^.GetPos<>OldPos then
+                  F^.Seek(OldPos);
+              end
+            end
+          else
+            { process leaf page }
+            if (0<=SubPageNo) then
+             if (ScannedPages=nil) or (ScannedPages^.Contains(SubPageNo)=false) then
+              begin
+                OldPos:=F^.GetPos;
+                OK:=ProcessLeafPage(PagesBase,PageSize,SubPageNo,TotalPages,ReadLeafEntryMethod,ScannedPages);
+                if F^.GetPos<>OldPos then
+                  F^.Seek(OldPos);
+              end;
+        if TreeDone then
+          Break;
+      end;
+    end;
+
+    if (BIH.PrevPage>0) and (TreeDone=false) then
+      CurPage:=BIH.PrevPage
+    else
+      Break;
+  until (OK=false) or TreeDone;
+  ProcessIndexPage:=OK;
+end;
+
+function TWinHelpFile.ProcessTree(ReadIndexEntryMethod, ReadLeafEntryMethod: pointer; NoDuplicates: boolean): boolean;
+var BTH: TWinHelpBTreeHeader;
+    OK: boolean;
+    PagesBase: longint;
+    ScannedPages: PIntCollection;
+begin
+  ScannedPages:=nil;
+  TreeDone:=false;
+  F^.Read(BTH,sizeof(BTH));
+  OK:=(F^.Status=stOK);
+  PagesBase:=F^.GetPos;
+  if OK then
+  begin
+    OK:=(BTH.Magic=WinHelpBTreeHeaderMagicNo) and
+        (BTH.MustBeZero=0) and
+        (BTH.MustBeNegOne=-1);
+  end;
+  if OK then
+  begin
+    if NoDuplicates then
+      New(ScannedPages, Init(500,100));
+    if BTH.NumLevels>1 then
+      begin
+        OK:=ProcessIndexPage(1,BTH.NumLevels,PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages,
+             ReadIndexEntryMethod,ReadLeafEntryMethod,ScannedPages);
+      end
+    else
+      OK:=ProcessLeafPage(PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages,ReadLeafEntryMethod,ScannedPAges);
+    if Assigned(ScannedPages) then
+      Dispose(ScannedPages, Done);
+  end;
+  ProcessTree:=OK;
+end;
+
+(*function TWinHelpFile.IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
+var OK: boolean;
+begin
+  OK:=true;
+  if FileName='|SYSTEM' then
+    begin
+      F^.Seek(FileOfs); OK:=(F^.Status=stOK);
+      if OK then OK:=ReadSystemFile;
+    end else
+  if (FileName='|Phrases') then
+    begin
+      PhrasesStart:=FileOfs;
+    end else
+  ;
+  IDIRProcessFile:=OK;
+end;
+
+function TWinHelpFile.IDIRProcessLeafPage(PagesBase: longint; PageSize,PageNo,TotalPages: word): boolean;
+var OK: boolean;
+    BNH: TWinHelpBTreeNodeHeader;
+    FileOfs: longint;
+    Count: integer;
+    S: string;
+    CurOfs,PageOfs,OldPos: longint;
+begin
+  PageOfs:=PagesBase+PageSize*PageNo;
+  F^.Seek(PageOfs);
+  OK:=(F^.Status=stOK);
+  repeat
+    if OK then
+    begin
+      F^.Read(BNH,sizeof(BNH));
+      OK:=(F^.Status=stOK);
+    end;
+    if OK then
+    begin
+      Count:=0;
+      repeat
+        S:=ReadString(F);
+        F^.Read(FileOfs,sizeof(FileOfs)); { longint }
+        OK:=OK and (F^.Status=stOK);
+        if OK then
+        begin
+          OldPos:=F^.GetPos;
+          OK:=IDIRProcessFile(S,FileOfs);
+          if F^.GetPos<>OldPos then
+            F^.Seek(OldPos);
+        end;
+        Inc(Count);
+      until (OK=false) or (Count=BNH.NumEntries){ or (F^.GetPos>=PageOfs+PageSize-BNH.NumEntries)};
+    end;
+
+    if BNH.PrevPage<>-1 then
+      begin
+        F^.Seek(PagesBase+PageSize*BNH.PrevPage);
+        OK:=(F^.Status=stOK);
+      end;
+  until (OK=false) or (BNH.PrevPage=-1);
+  IDIRProcessLeafPage:=OK;
+end;
+
+function TWinHelpFile.IDIRProcessIndexPage(CurLevel,MaxLevel: integer; PagesBase: longint; PageSize: word; PageNo,
+         TotalPages: word): boolean;
+var BIH: TWinHelpBTreeIndexHeader;
+    I: integer;
+    SubPageNo: integer;
+    OK: boolean;
+    S: string;
+    OldPos: longint;
+begin
+  F^.Seek(PagesBase+PageSize*PageNo);
+  OK:=(F^.Status=stOK);
+  repeat
+    if OK then
+    begin
+      F^.Read(BIH,sizeof(BIH));
+      OK:=(F^.Status=stOK);
+    end;
+    if OK then
+    for I:=1 to BIH.NumEntries do
+    begin
+      S:=ReadString(F);
+      F^.Read(SubPageNo,sizeof(SubPageNo)); { word }
+      OK:=OK and (F^.Status=stOK);
+      if OK then
+        if CurLevel<MaxLevel-1 then
+          begin
+            if (0<=SubPageNo) then
+            begin
+              OldPos:=F^.GetPos;
+              OK:=IDIRProcessIndexPage(CurLevel+1,MaxLevel,PagesBase,PageSize,SubPageNo,TotalPages);
+              if F^.GetPos<>OldPos then
+                F^.Seek(OldPos);
+            end
+          end
+        else
+          { process leaf page }
+          if (0<=SubPageNo) then
+            OK:=IDIRProcessLeafPage(PagesBase,PageSize,SubPageNo,TotalPages);
+    end;
+
+    if (BIH.PrevPage>0) then
+      begin
+        F^.Seek(PagesBase+PageSize*BIH.PrevPage);
+        OK:=(F^.Status=stOK);
+      end
+    else
+      Break;
+  until (OK=false);
+  IDIRProcessIndexPage:=OK;
+end;
+
+function TWinHelpFile.ReadInternalDirectory: boolean;
+var BTH: TWinHelpBTreeHeader;
+    OK: boolean;
+    PagesBase: longint;
+begin
+  F^.Read(BTH,sizeof(BTH));
+  OK:=(F^.Status=stOK);
+  PagesBase:=F^.GetPos;
+  if OK then
+  begin
+    OK:=(BTH.Magic=WinHelpBTreeHeaderMagicNo) and
+        (BTH.MustBeZero=0) and
+        (BTH.MustBeNegOne=-1);
+  end;
+  if BTH.NumLevels>1 then
+    OK:=IDIRProcessIndexPage(1,BTH.NumLevels,PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages)
+  else
+    OK:=IDIRProcessLeafPage(PagesBase,BTH.PageSize,BTH.RootPage,BTH.TotalPages);
+  ReadInternalDirectory:=OK;
+end;
+*)
+function TWinHelpFile.IDIRProcessFile(const FileName: string; FileOfs: longint): boolean;
+var OK: boolean;
+begin
+  OK:=true;
+  if FileName='|SYSTEM' then
+    begin
+      F^.Seek(FileOfs); OK:=(F^.Status=stOK);
+      if OK then OK:=ReadSystemFile;
+    end else
+  if (FileName='|Phrases') then
+    begin
+      PhrasesStart:=FileOfs;
+    end else
+  ;
+  if (FileName='|TOPIC') then
+    begin
+      TopicFileStart:=FileOfs;
+    end else
+  ;
+  if (FileName='|TTLBTREE') then
+    begin
+      TTLBTreeStart:=FileOfs;
+    end else
+  ;
+  IDIRProcessFile:=OK;
+end;
+
+function TWinHelpFile.IDIRReadIndexEntry(SubPageNo: PInteger): boolean;
+var {S: string;}
+    OK: boolean;
+begin
+  {S:=}ReadString(F);
+  F^.Read(SubPageNo^,sizeof(SubPageNo^));
+  OK:=(F^.Status=stOK);
+  IDIRReadIndexEntry:=OK;
+end;
+
+function TWinHelpFile.IDIRReadLeafEntry(P: pointer): boolean;
+var OK: boolean;
+    S: string;
+    FileOfs,OldPos: longint;
+begin
+  S:=ReadString(F);
+  F^.Read(FileOfs,sizeof(FileOfs)); { longint }
+  OK:=(F^.Status=stOK);
+  if OK then
+  begin
+    OldPos:=F^.GetPos;
+    OK:=IDIRProcessFile(S,FileOfs);
+    if F^.GetPos<>OldPos then
+      F^.Seek(OldPos);
+    OK:=OK and (F^.Status=stOK);
+  end;
+  IDIRReadLeafEntry:=OK;
+end;
+
+function TWinHelpFile.ReadInternalDirectory: boolean;
+var OK: boolean;
+    FH: TWinHelpFileEntryHeader;
+begin
+  F^.Read(FH,sizeof(FH));
+  OK:=(F^.Status=stOK);
+  if OK then
+    OK:=ProcessTree(@TWinHelpFile.IDIRReadIndexEntry,@TWinHelpFile.IDIRReadLeafEntry,true);
+  ReadInternalDirectory:=OK;
+end;
+
+function TWinHelpFile.TTLBReadIndexEntry(SubPageNo: PInteger): boolean;
+var TopicOffset: longint;
+    OK: boolean;
+begin
+  F^.Read(TopicOffset,sizeof(TopicOffset));
+  F^.Read(SubPageNo^,sizeof(SubPageNo^));
+  OK:=(F^.Status=stOK);
+  TTLBReadIndexEntry:=OK;
+end;
+
+function TWinHelpFile.TTLBReadLeafEntry(P: pointer): boolean;
+var OK: boolean;
+    S: string;
+    TopicOfs,OldPos: longint;
+begin
+  F^.Read(TopicOfs,sizeof(TopicOfs)); { longint }
+  S:=ReadString(F);
+  OK:=(F^.Status=stOK);
+  if OK then
+  begin
+    OldPos:=F^.GetPos;
+    OK:=TTLBProcessTopicEntry(S,TopicOfs);
+    if F^.GetPos<>OldPos then
+      F^.Seek(OldPos);
+    OK:=OK and (F^.Status=stOK);
+  end;
+  TTLBReadLeafEntry:=OK;
+end;
+
+function TWinHelpFile.TTLBProcessTopicEntry(const TopicTitle: string; FileOfs: longint): boolean;
+var OK: boolean;
+const Count: longint = 0;
+begin
+  Inc(Count);
+  if (Count mod 100)=1 then
+  begin
+    gotoxy(1,1); write(Count,' - ',IndexEntries^.Count,' - ',Topics^.Count);
+  end;
+  OK:=(IndexEntries^.Count<MaxCollectionSize-10);
+  if OK then
+  begin
+    if (TopicTitle<>'') and (FileOfs>=0) then
+    begin
+      AddIndexEntry(TopicTitle,FileOfs);
+      AddTopic(FileOfs,FileOfs,'');
+    end;
+  end;
+  TTLBProcessTopicEntry:=OK;
+end;
+
+function TWinHelpFile.ReadTTLBTree: boolean;
+var OK: boolean;
+    FH: TWinHelpFileEntryHeader;
+begin
+  F^.Read(FH,sizeof(FH));
+  OK:=(F^.Status=stOK);
+  if OK then
+    OK:=ProcessTree(@TWinHelpFile.TTLBReadIndexEntry,@TWinHelpFile.TTLBReadLeafEntry,true);
+  ReadTTLBTree:=OK;
+end;
+
+function TWinHelpFile.LoadIndex: boolean;
+var OK: boolean;
+begin
+  if IndexLoaded then OK:=true else
+  begin
+    if PhrasesStart<>0 then
+    begin
+      F^.Seek(PhrasesStart); OK:=(F^.Status=stOK);
+      if OK then OK:=ReadPhraseFile;
+    end;
+    if TTLBTreeStart<>0 then
+    begin
+      F^.Seek(TTLBTreeStart); OK:=(F^.Status=stOK);
+      if OK then OK:=ReadTTLBTree;
+    end;
+    IndexLoaded:=OK;
+  end;
+  LoadIndex:=OK;
+end;
+
+procedure TWinHelpFile.ExtractTopicOffset(TopicOffset: longint; var TopicBlockNo, TopicBlockOffset: word);
+var {OfsBitCount: longint;
+    OfsMask: longint;}
+    BS: longint;
+begin
+  if LZ77Compressed then
+    BS:=16384
+  else
+    BS:=TopicBlockSize;
+{  for OfsBitCount:=0 to 31 do
+   if (1 shl OfsBitCount)=BS then
+     Break;
+  OfsMask:=(1 shl OfsBitCount)-1;
+  TopicBlockNo:=(TopicOffset and not OfsMask) shr OfsBitCount;
+  TopicBlockOffset:=(TopicOffset and OfsMask);}
+  TopicBlockNo:=TopicOffset div BS;
+  TopicBlockOffset:=TopicOffset mod BS;
+end;
+
+function TWinHelpFile.ReadTopicBlock(BlockNo: word; var T: TTopicBlock; ReadData: boolean): boolean;
+var TempBuf: pointer;
+    BlockBuf: ^TWinHelpTopicBlock;
+    OK: boolean;
+    RS,DecompSize: longint;
+const TempBufSize = 16384;
+begin
+  FillChar(T,sizeof(T),0);
+  F^.Seek(TopicFileStart+sizeof(TWinHelpFileEntryHeader)+longint(BlockNo)*TopicBlockSize);
+  OK:=(F^.Status=stOK);
+  if OK then
+ if ReadData=false then
+  begin
+    F^.Read(T.Header,sizeof(T.Header));
+    OK:=(F^.Status=stOK);
+  end
+ else
+  begin
+    GetMem(BlockBuf, TopicBlockSize);
+    F^.Read(BlockBuf^,TopicBlockSize);
+    OK:=(F^.Status=stOK);
+    if OK then
+    begin
+      Move(BlockBuf^.Header,T.Header,sizeof(T.Header));
+      if LZ77Compressed then
+        begin
+          GetMem(TempBuf,TempBufSize);
+          DecompSize:=LZ77Decompress(@BlockBuf^.Data,TopicBlockSize-sizeof(BlockBuf^.Header),TempBuf,TempBufSize);
+          T.DataSize:=DecompSize;
+          GetMem(T.DataPtr,T.DataSize);
+          Move(TempBuf^,T.DataPtr^,T.DataSize);
+          FreeMem(TempBuf,TempBufSize);
+        end
+      else
+        begin
+          T.DataSize:=TopicBlockSize-sizeof(BlockBuf^.Header);
+          GetMem(T.DataPtr,T.DataSize);
+          Move(BlockBuf^.Data,T.DataPtr^,T.DataSize);
+        end;
+    end;
+    FreeMem(BlockBuf,TopicBlockSize);
+  end;
+  ReadTopicBlock:=OK;
+end;
+
+procedure FreeTopicBlock(T: TTopicBlock);
+begin
+  if (T.DataSize>0) and (T.DataPtr<>nil) then
+  begin
+    FreeMem(T.DataPtr,T.DataSize);
+    T.DataPtr:=nil;
+  end;
+end;
+
+function TWinHelpFile.ReadTopic(T: PTopic): boolean;
+var OK: boolean;
+    BlockNo,BlockOfs: word;
+    TB: TTopicBlock;
+begin
+  OK:=(TopicFileStart<>0) and (T<>nil);
+  if OK then
+  begin
+    ExtractTopicOffset(T^.FileOfs,BlockNo,BlockOfs);
+    OK:=ReadTopicBlock(BlockNo,TB,true);
+  end;
+  if OK then
+  begin
+    FreeTopicBlock(TB);
+  end;
+  ReadTopic:=OK;
+end;
+
+destructor TWinHelpFile.Done;
+begin
+  if Assigned(F) then Dispose(F, Done); F:=nil;
+  if Assigned(Phrases) then Dispose(Phrases, Done); Phrases:=nil;
+  inherited Done;
+end;
+
+END.
+{
+  $Log$
+  Revision 1.1  2000-06-26 07:29:23  pierre
+   * new bunch of Gabor's changes
+
+
+}