home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / extra18 / ide / helpfile.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1991-06-21  |  22.2 KB  |  847 lines

  1. {************************************************}
  2. {                                                }
  3. {   Turbo Pascal 6.0                             }
  4. {   Turbo Vision Demo                            }
  5. {   Copyright (c) 1990 by Borland International  }
  6. {                                                }
  7. {************************************************}
  8.  
  9. UNIT HelpFile;
  10.  
  11. {$F+,O+,X+,S-,D-}
  12.  
  13. INTERFACE
  14.  
  15. USES Objects, Drivers, Views;
  16.  
  17. CONST
  18.   CHelpColor = #$37#$3F#$3A#$13#$13#$30#$3E#$1E;
  19.   CHelpBlackWhite = #$07#$0F#$07#$70#$70#$07#$0F#$70;
  20.   CHelpMonochrome = #$07#$0F#$07#$70#$70#$07#$0F#$70;
  21.   CHelpViewer = #6#7#8;
  22.   CHelpWindow = #64#65#66#67#68#69#70#71;
  23.  
  24. TYPE
  25.  
  26.   { TParagraph }
  27.  
  28.   PParagraph = ^TParagraph;
  29.   TParagraph = RECORD
  30.                  Next : PParagraph;
  31.                  Wrap : BOOLEAN;
  32.                  Size : WORD;
  33.                  TEXT : RECORD END;
  34.                END;
  35.  
  36.   { THelpTopic }
  37.  
  38.   TCrossRef = RECORD
  39.                 Ref : INTEGER;
  40.                 Offset : INTEGER;
  41.                 LENGTH : BYTE;
  42.               END;
  43.  
  44.   PCrossRefs = ^TCrossRefs;
  45.   TCrossRefs = ARRAY[1..10000] OF TCrossRef;
  46.   TCrossRefHandler = PROCEDURE(VAR S : TStream; XRefValue : INTEGER);
  47.  
  48.   PHelpTopic = ^THelpTopic;
  49.   THelpTopic = OBJECT(TObject)
  50.                  CONSTRUCTOR Init;
  51.                  CONSTRUCTOR Load(VAR S : TStream);
  52.                  DESTRUCTOR Done; VIRTUAL;
  53.                  PROCEDURE AddCrossRef(Ref : TCrossRef);
  54.                  PROCEDURE AddParagraph(P : PParagraph);
  55.                  PROCEDURE GetCrossRef(I : INTEGER; VAR Loc : TPoint; VAR LENGTH : BYTE;
  56.                                        VAR Ref : INTEGER);
  57.                  FUNCTION GetLine(Line : INTEGER) : STRING;
  58.                  FUNCTION GetNumCrossRefs : INTEGER;
  59.                  FUNCTION NumLines : INTEGER;
  60.                  PROCEDURE SetCrossRef(I : INTEGER; VAR Ref : TCrossRef);
  61.                  PROCEDURE SetNumCrossRefs(I : INTEGER);
  62.                  PROCEDURE SetWidth(AWidth : INTEGER);
  63.                  PROCEDURE Store(VAR S : TStream);
  64.                  PRIVATE
  65.                  Paragraphs : PParagraph;
  66.                  NumRefs : INTEGER;
  67.                  CrossRefs : PCrossRefs;
  68.                  Width : INTEGER;
  69.                  LastOffset : INTEGER;
  70.                  LastLine : INTEGER;
  71.                  LastParagraph : PParagraph;
  72.                  FUNCTION WrapText(VAR TEXT; Size : INTEGER; VAR Offset : INTEGER;
  73.                                    Wrap : BOOLEAN) : STRING;
  74.                END;
  75.  
  76.   { THelpIndex }
  77.  
  78.   PIndexArray = ^TIndexArray;
  79.   TIndexArray = ARRAY[0..16380] OF LONGINT;
  80.  
  81.   PHelpIndex = ^THelpIndex;
  82.   THelpIndex = OBJECT(TObject)
  83.                  Size : WORD;
  84.                  Index : PIndexArray;
  85.                  CONSTRUCTOR Init;
  86.                  CONSTRUCTOR Load(VAR S : TStream);
  87.                  DESTRUCTOR Done; VIRTUAL;
  88.                  FUNCTION Position(I : INTEGER) : LONGINT;
  89.                  PROCEDURE Add(I : INTEGER; VAL : LONGINT);
  90.                  PROCEDURE Store(VAR S : TStream);
  91.                END;
  92.  
  93.   { THelpFile }
  94.  
  95.   PHelpFile = ^THelpFile;
  96.   THelpFile = OBJECT(TObject)
  97.                 Stream : PStream;
  98.                 Modified : BOOLEAN;
  99.                 CONSTRUCTOR Init(S : PStream);
  100.                 DESTRUCTOR Done; VIRTUAL;
  101.                 FUNCTION GetTopic(I : INTEGER) : PHelpTopic;
  102.                 FUNCTION InvalidTopic : PHelpTopic;
  103.                 PROCEDURE RecordPositionInIndex(I : INTEGER);
  104.                 PROCEDURE PutTopic(Topic : PHelpTopic);
  105.                 PRIVATE
  106.                 Index : PHelpIndex;
  107.                 IndexPos : LONGINT;
  108.               END;
  109.  
  110.   { THelpViewer }
  111.  
  112.   PHelpViewer = ^THelpViewer;
  113.   THelpViewer = OBJECT(TScroller)
  114.                   HFile : PHelpFile;
  115.                   Topic : PHelpTopic;
  116.                   Selected : INTEGER;
  117.                   CONSTRUCTOR Init(VAR Bounds : TRect; AHScrollBar,
  118.                                    AVScrollBar : PScrollBar; AHelpFile : PHelpFile; Context : WORD);
  119.                   DESTRUCTOR Done; VIRTUAL;
  120.                   PROCEDURE ChangeBounds(VAR Bounds : TRect); VIRTUAL;
  121.                   PROCEDURE Draw; VIRTUAL;
  122.                   FUNCTION GetPalette : PPalette; VIRTUAL;
  123.                   PROCEDURE HandleEvent(VAR Event : TEvent); VIRTUAL;
  124.                 END;
  125.  
  126.   { THelpWindow }
  127.  
  128.   PHelpWindow = ^THelpWindow;
  129.   THelpWindow = OBJECT(TWindow)
  130.                   CONSTRUCTOR Init(HFile : PHelpFile; Context : WORD);
  131.                   FUNCTION GetPalette : PPalette; VIRTUAL;
  132.                 END;
  133.  
  134. CONST
  135.  
  136.   RHelpTopic : TStreamRec = (
  137.     ObjType : 10000;
  138.     VmtLink : OFS(TypeOf(THelpTopic)^);
  139.     Load : @THelpTopic.Load;
  140.     Store : @THelpTopic.Store
  141.     );
  142.   RHelpIndex : TStreamRec = (
  143.     ObjType : 10001;
  144.     VmtLink : OFS(TypeOf(THelpIndex)^);
  145.     Load : @THelpIndex.Load;
  146.     Store : @THelpIndex.Store
  147.     );
  148.  
  149. PROCEDURE RegisterHelpFile;
  150.  
  151. PROCEDURE NotAssigned(VAR S : TStream; Value : INTEGER);
  152.  
  153. CONST
  154.   CrossRefHandler : TCrossRefHandler = NotAssigned;
  155.  
  156. IMPLEMENTATION
  157.  
  158.   { THelpTopic }
  159.  
  160.   CONSTRUCTOR THelpTopic.Init;
  161.   BEGIN
  162.     TObject.Init;
  163.     LastLine := MAXINT;
  164.   END;
  165.  
  166.   CONSTRUCTOR THelpTopic.Load(VAR S : TStream);
  167.  
  168.     PROCEDURE ReadParagraphs;
  169.     VAR
  170.       I, Size : INTEGER;
  171.       PP : ^PParagraph;
  172.     BEGIN
  173.       S.READ(I, SIZEOF(I));
  174.       PP := @Paragraphs;
  175.       WHILE I > 0 DO
  176.         BEGIN
  177.           S.READ(Size, SIZEOF(Size));
  178.           GETMEM(PP^, SIZEOF(PP^^)+Size);
  179.           PP^^.Size := Size;
  180.           S.READ(PP^^.Wrap, SIZEOF(BOOLEAN));
  181.           S.READ(PP^^.TEXT, Size);
  182.           PP := @PP^^.Next;
  183.           DEC(I);
  184.         END;
  185.       PP^ := NIL;
  186.     END;
  187.  
  188.     PROCEDURE ReadCrossRefs;
  189.     BEGIN
  190.       S.READ(NumRefs, SIZEOF(INTEGER));
  191.       GETMEM(CrossRefs, SIZEOF(TCrossRef)*NumRefs);
  192.       S.READ(CrossRefs^, SIZEOF(TCrossRef)*NumRefs);
  193.     END;
  194.  
  195.   BEGIN
  196.     ReadParagraphs;
  197.     ReadCrossRefs;
  198.     Width := 0;
  199.     LastLine := MAXINT;
  200.   END;
  201.  
  202.   DESTRUCTOR THelpTopic.Done;
  203.  
  204.     PROCEDURE DisposeParagraphs;
  205.     VAR
  206.       P, T : PParagraph;
  207.     BEGIN
  208.       P := Paragraphs;
  209.       WHILE P <> NIL DO
  210.         BEGIN
  211.           T := P;
  212.           P := P^.Next;
  213.           FREEMEM(T, SIZEOF(T^)+T^.Size);
  214.         END;
  215.     END;
  216.  
  217.   BEGIN
  218.     DisposeParagraphs;
  219.     FREEMEM(CrossRefs, SIZEOF(TCrossRef)*NumRefs);
  220.     TObject.Done
  221.   END;
  222.  
  223.   PROCEDURE THelpTopic.AddCrossRef(Ref : TCrossRef);
  224.   VAR
  225.     P : PCrossRefs;
  226.   BEGIN
  227.     GETMEM(P, (NumRefs+1)*SIZEOF(TCrossRef));
  228.     IF NumRefs > 0 THEN
  229.       BEGIN
  230.         MOVE(CrossRefs^, P^, NumRefs*SIZEOF(TCrossRef));
  231.         FREEMEM(CrossRefs, NumRefs*SIZEOF(TCrossRef));
  232.       END;
  233.     CrossRefs^[NumRefs] := Ref;
  234.     INC(NumRefs);
  235.   END;
  236.  
  237.   PROCEDURE THelpTopic.AddParagraph(P : PParagraph);
  238.   VAR
  239.     PP : ^PParagraph;
  240.   BEGIN
  241.     PP := @Paragraphs;
  242.     WHILE PP^ <> NIL DO
  243.       PP := @PP^^.Next;
  244.     PP^ := P;
  245.     P^.Next := NIL;
  246.   END;
  247.  
  248.   PROCEDURE THelpTopic.GetCrossRef(I : INTEGER; VAR Loc : TPoint;
  249.                                    VAR LENGTH : BYTE; VAR Ref : INTEGER);
  250.   VAR
  251.     OldOffset, CurOffset, Offset, ParaOffset : INTEGER;
  252.     P : PParagraph;
  253.     Line : INTEGER;
  254.   BEGIN
  255.     ParaOffset := 0;
  256.     CurOffset := 0;
  257.     OldOffset := 0;
  258.     Line := 0;
  259.     Offset := CrossRefs^[I].Offset;
  260.     P := Paragraphs;
  261.     WHILE ParaOffset+CurOffset < Offset DO
  262.       BEGIN
  263.         OldOffset := ParaOffset+CurOffset;
  264.         WrapText(P^.TEXT, P^.Size, CurOffset, P^.Wrap);
  265.         INC(Line);
  266.         IF CurOffset >= P^.Size THEN
  267.           BEGIN
  268.             INC(ParaOffset, P^.Size);
  269.             P := P^.Next;
  270.             CurOffset := 0;
  271.           END;
  272.       END;
  273.     Loc.X := Offset-OldOffset-1;
  274.     Loc.Y := Line;
  275.     LENGTH := CrossRefs^[I].LENGTH;
  276.     Ref := CrossRefs^[I].Ref;
  277.   END;
  278.  
  279.   FUNCTION THelpTopic.GetLine(Line : INTEGER) : STRING;
  280.   VAR
  281.     Offset, I : INTEGER;
  282.     P : PParagraph;
  283.   BEGIN
  284.     IF LastLine < Line THEN
  285.       BEGIN
  286.         I := Line;
  287.         DEC(Line, LastLine);
  288.         LastLine := I;
  289.         Offset := LastOffset;
  290.         P := LastParagraph;
  291.       END
  292.     ELSE
  293.       BEGIN
  294.         P := Paragraphs;
  295.         Offset := 0;
  296.         LastLine := Line;
  297.       END;
  298.     GetLine := '';
  299.     WHILE (P <> NIL) DO
  300.       BEGIN
  301.         WHILE Offset < P^.Size DO
  302.           BEGIN
  303.             DEC(Line);
  304.             GetLine := WrapText(P^.TEXT, P^.Size, Offset, P^.Wrap);
  305.             IF Line = 0 THEN
  306.               BEGIN
  307.                 LastOffset := Offset;
  308.                 LastParagraph := P;
  309.                 EXIT;
  310.               END;
  311.           END;
  312.         P := P^.Next;
  313.         Offset := 0;
  314.       END;
  315.     GetLine := '';
  316.   END;
  317.  
  318.   FUNCTION THelpTopic.GetNumCrossRefs : INTEGER;
  319.   BEGIN
  320.     GetNumCrossRefs := NumRefs;
  321.   END;
  322.  
  323.   FUNCTION THelpTopic.NumLines : INTEGER;
  324.   VAR
  325.     Offset, Lines : INTEGER;
  326.     P : PParagraph;
  327.   BEGIN
  328.     Offset := 0;
  329.     Lines := 0;
  330.     P := Paragraphs;
  331.     WHILE P <> NIL DO
  332.       BEGIN
  333.         Offset := 0;
  334.         WHILE Offset < P^.Size DO
  335.           BEGIN
  336.             INC(Lines);
  337.             WrapText(P^.TEXT, P^.Size, Offset, P^.Wrap);
  338.           END;
  339.         P := P^.Next;
  340.       END;
  341.     NumLines := Lines;
  342.   END;
  343.  
  344.   PROCEDURE THelpTopic.SetCrossRef(I : INTEGER; VAR Ref : TCrossRef);
  345.   BEGIN
  346.     IF I <= NumRefs THEN CrossRefs^[I] := Ref;
  347.   END;
  348.  
  349.   PROCEDURE THelpTopic.SetNumCrossRefs(I : INTEGER);
  350.   VAR
  351.     P : PCrossRefs;
  352.   BEGIN
  353.     IF NumRefs = I THEN EXIT;
  354.     GETMEM(P, I*SIZEOF(TCrossRef));
  355.     IF NumRefs > 0 THEN
  356.       BEGIN
  357.         IF I > NumRefs THEN MOVE(CrossRefs^, P^, NumRefs*SIZEOF(TCrossRef))
  358.         ELSE MOVE(CrossRefs^, P^, I*SIZEOF(TCrossRef));
  359.         FREEMEM(CrossRefs, NumRefs*SIZEOF(TCrossRef));
  360.       END;
  361.     CrossRefs := P;
  362.     NumRefs := I;
  363.   END;
  364.  
  365.   PROCEDURE THelpTopic.SetWidth(AWidth : INTEGER);
  366.   BEGIN
  367.     Width := AWidth;
  368.   END;
  369.  
  370.   PROCEDURE THelpTopic.Store(VAR S : TStream);
  371.  
  372.     PROCEDURE WriteParagraphs;
  373.     VAR
  374.       I : INTEGER;
  375.       P : PParagraph;
  376.     BEGIN
  377.       P := Paragraphs;
  378.       I := 0;
  379.       WHILE P <> NIL DO
  380.         BEGIN
  381.           INC(I);
  382.           P := P^.Next;
  383.         END;
  384.       S.WRITE(I, SIZEOF(I));
  385.       P := Paragraphs;
  386.       WHILE P <> NIL DO
  387.         BEGIN
  388.           S.WRITE(P^.Size, SIZEOF(INTEGER));
  389.           S.WRITE(P^.Wrap, SIZEOF(BOOLEAN));
  390.           S.WRITE(P^.TEXT, P^.Size);
  391.           P := P^.Next;
  392.         END;
  393.     END;
  394.  
  395.     PROCEDURE WriteCrossRefs;
  396.     VAR
  397.       I : INTEGER;
  398.     BEGIN
  399.       S.WRITE(NumRefs, SIZEOF(INTEGER));
  400.       IF @CrossRefHandler = @NotAssigned THEN
  401.         S.WRITE(CrossRefs^, SIZEOF(TCrossRef)*NumRefs)
  402.       ELSE
  403.         FOR I := 1 TO NumRefs DO
  404.           BEGIN
  405.             CrossRefHandler(S, CrossRefs^[I].Ref);
  406.             S.WRITE(CrossRefs^[I].Offset, SIZEOF(INTEGER)+SIZEOF(BYTE));
  407.           END;
  408.     END;
  409.  
  410.   BEGIN
  411.     WriteParagraphs;
  412.     WriteCrossRefs;
  413.   END;
  414.  
  415.   FUNCTION THelpTopic.WrapText(VAR TEXT; Size : INTEGER;
  416.                                VAR Offset : INTEGER; Wrap : BOOLEAN) : STRING;
  417.   TYPE
  418.     PCArray = ^CArray;
  419.     CArray = ARRAY[0..32767] OF CHAR;
  420.   VAR
  421.     Line : STRING;
  422.     I, P : INTEGER;
  423.  
  424.     FUNCTION IsBlank(Ch : CHAR) : BOOLEAN;
  425.     BEGIN
  426.       IsBlank := (Ch = ' ') OR(Ch = #13) OR(Ch = #10);
  427.     END;
  428.  
  429.     FUNCTION Scan(VAR P; Offset : INTEGER; C : CHAR) : INTEGER; ASSEMBLER;
  430.     ASM
  431.       CLD
  432.       LES     DI,P
  433.       ADD     DI,&Offset
  434.       MOV     CX,256
  435.       MOV     AL, C
  436.       REPNE   SCASB
  437.       SUB     CX,256
  438.       NEG     CX
  439.       XCHG    AX,CX
  440.     END;
  441.  
  442.     PROCEDURE TextToLine(VAR TEXT; Offset, LENGTH : INTEGER; VAR Line : STRING);
  443.       ASSEMBLER;
  444.     ASM
  445.       CLD
  446.       PUSH    DS
  447.       LDS     SI,Text
  448.       ADD     SI,&Offset
  449.       LES     DI,Line
  450.       MOV     AX,Length
  451.       STOSB
  452.       XCHG    AX,CX
  453.       REP     MOVSB
  454.       POP     DS
  455.     END;
  456.  
  457.   BEGIN
  458.     I := Scan(TEXT, Offset, #13);
  459.     IF I+Offset > Size THEN I := Size-Offset;
  460.     IF (I >= Width) AND Wrap THEN
  461.       BEGIN
  462.         I := Offset+Width;
  463.         IF I > Size THEN I := Size
  464.         ELSE
  465.           BEGIN
  466.             WHILE (I > Offset) AND NOT IsBlank(PCArray(@TEXT)^[I]) DO DEC(I);
  467.             IF I = Offset THEN I := Offset+Width
  468.             ELSE INC(I);
  469.           END;
  470.         IF I = Offset THEN I := Offset+Width;
  471.         DEC(I, Offset);
  472.       END;
  473.     TextToLine(TEXT, Offset, I, Line);
  474.     IF Line[LENGTH(Line)] = #13 THEN DEC(Line[0]);
  475.     INC(Offset, I);
  476.     WrapText := Line;
  477.   END;
  478.  
  479.   { THelpIndex }
  480.  
  481.   CONSTRUCTOR THelpIndex.Init;
  482.   BEGIN
  483.     TObject.Init;
  484.     Size := 0;
  485.     Index := NIL;
  486.   END;
  487.  
  488.   CONSTRUCTOR THelpIndex.Load(VAR S : TStream);
  489.   BEGIN
  490.     S.READ(Size, SIZEOF(Size));
  491.     IF Size = 0 THEN Index := NIL
  492.     ELSE
  493.       BEGIN
  494.         GETMEM(Index, SIZEOF(LONGINT)*Size);
  495.         S.READ(Index^, SIZEOF(LONGINT)*Size);
  496.       END;
  497.   END;
  498.  
  499.   DESTRUCTOR THelpIndex.Done;
  500.   BEGIN
  501.     FREEMEM(Index, SIZEOF(LONGINT)*Size);
  502.     TObject.Done;
  503.   END;
  504.  
  505.   FUNCTION THelpIndex.Position(I : INTEGER) : LONGINT;
  506.   BEGIN
  507.     IF I < Size THEN Position := Index^[I]
  508.     ELSE Position := -1;
  509.   END;
  510.  
  511.   PROCEDURE THelpIndex.Add(I : INTEGER; VAL : LONGINT);
  512.   CONST
  513.     Delta = 10;
  514.   VAR
  515.     P : PIndexArray;
  516.     NewSize : INTEGER;
  517.   BEGIN
  518.     IF I >= Size THEN
  519.       BEGIN
  520.         NewSize := (I+Delta) DIV Delta*Delta;
  521.         GETMEM(P, NewSize*SIZEOF(LONGINT));
  522.         IF P <> NIL THEN
  523.           BEGIN
  524.             MOVE(Index^, P^, Size*SIZEOF(LONGINT));
  525.             FILLCHAR(P^[Size], (NewSize-Size)*SIZEOF(LONGINT), $FF);
  526.           END;
  527.         IF Size > 0 THEN FREEMEM(Index, Size*SIZEOF(LONGINT));
  528.         Index := P;
  529.         Size := NewSize;
  530.       END;
  531.     Index^[I] := VAL;
  532.   END;
  533.  
  534.   PROCEDURE THelpIndex.Store(VAR S : TStream);
  535.   BEGIN
  536.     S.WRITE(Size, SIZEOF(Size));
  537.     S.WRITE(Index^, SIZEOF(LONGINT)*Size);
  538.   END;
  539.  
  540.   { THelpFile }
  541.  
  542. CONST
  543.   MagicHeader = $46484246;        {'FBHF'}
  544.  
  545.   CONSTRUCTOR THelpFile.Init(S : PStream);
  546.   VAR
  547.     Magic : LONGINT;
  548.   BEGIN
  549.     Magic := 0;
  550.     S^.SEEK(0);
  551.     IF S^.GetSize > SIZEOF(Magic) THEN
  552.       S^.READ(Magic, SIZEOF(Magic));
  553.     IF Magic <> MagicHeader THEN
  554.       BEGIN
  555.         IndexPos := 12;
  556.         S^.SEEK(IndexPos);
  557.         Index := NEW(PHelpIndex, Init);
  558.         Modified := TRUE;
  559.       END
  560.     ELSE
  561.       BEGIN
  562.         S^.SEEK(8);
  563.         S^.READ(IndexPos, SIZEOF(IndexPos));
  564.         S^.SEEK(IndexPos);
  565.         Index := PHelpIndex(S^.Get);
  566.         Modified := FALSE;
  567.       END;
  568.     Stream := S;
  569.   END;
  570.  
  571.   DESTRUCTOR THelpFile.Done;
  572.   VAR
  573.     Magic, Size : LONGINT;
  574.   BEGIN
  575.     IF Modified THEN
  576.       BEGIN
  577.         Stream^.SEEK(IndexPos);
  578.         Stream^.Put(Index);
  579.         Stream^.SEEK(0);
  580.         Magic := MagicHeader;
  581.         Size := Stream^.GetSize-8;
  582.         Stream^.WRITE(Magic, SIZEOF(Magic));
  583.         Stream^.WRITE(Size, SIZEOF(Size));
  584.         Stream^.WRITE(IndexPos, SIZEOF(IndexPos));
  585.       END;
  586.     DISPOSE(Stream, Done);
  587.     DISPOSE(Index, Done);
  588.   END;
  589.  
  590.   FUNCTION THelpFile.GetTopic(I : INTEGER) : PHelpTopic;
  591.   VAR
  592.     POS : LONGINT;
  593.   BEGIN
  594.     POS := Index^.Position(I);
  595.     IF POS > 0 THEN
  596.       BEGIN
  597.         Stream^.SEEK(POS);
  598.         GetTopic := PHelpTopic(Stream^.Get);
  599.       END
  600.     ELSE GetTopic := InvalidTopic;
  601.   END;
  602.  
  603.   FUNCTION THelpFile.InvalidTopic : PHelpTopic;
  604.   VAR
  605.     Topic : PHelpTopic;
  606.     Para : PParagraph;
  607.   CONST
  608.     InvalidStr = #13' No help available in this context.';
  609.     InvalidText : ARRAY[1..LENGTH(InvalidStr)] OF CHAR = InvalidStr;
  610.   BEGIN
  611.     Topic := NEW(PHelpTopic, Init);
  612.     GETMEM(Para, SIZEOF(Para^)+SIZEOF(InvalidText));
  613.     Para^.Size := SIZEOF(InvalidText);
  614.     Para^.Wrap := FALSE;
  615.     Para^.Next := NIL;
  616.     MOVE(InvalidText, Para^.TEXT, SIZEOF(InvalidText));
  617.     Topic^.AddParagraph(Para);
  618.     InvalidTopic := Topic;
  619.   END;
  620.  
  621.   PROCEDURE THelpFile.RecordPositionInIndex(I : INTEGER);
  622.   BEGIN
  623.     Index^.Add(I, IndexPos);
  624.     Modified := TRUE;
  625.   END;
  626.  
  627.   PROCEDURE THelpFile.PutTopic(Topic : PHelpTopic);
  628.   BEGIN
  629.     Stream^.SEEK(IndexPos);
  630.     Stream^.Put(Topic);
  631.     IndexPos := Stream^.GetPos;
  632.     Modified := TRUE;
  633.   END;
  634.  
  635.   { THelpViewer }
  636.  
  637.   CONSTRUCTOR THelpViewer.Init(VAR Bounds : TRect; AHScrollBar,
  638.                                AVScrollBar : PScrollBar; AHelpFile : PHelpFile; Context : WORD);
  639.   BEGIN
  640.     TScroller.Init(Bounds, AHScrollBar, AVScrollBar);
  641.     Options := Options OR ofSelectable;
  642.     GrowMode := gfGrowHiX+gfGrowHiY;
  643.     HFile := AHelpFile;
  644.     Topic := AHelpFile^.GetTopic(Context);
  645.     Topic^.SetWidth(Size.X);
  646.     SetLimit(78, Topic^.NumLines);
  647.     Selected := 1;
  648.   END;
  649.  
  650.   DESTRUCTOR THelpViewer.Done;
  651.   BEGIN
  652.     TScroller.Done;
  653.     DISPOSE(HFile, Done);
  654.     DISPOSE(Topic, Done);
  655.   END;
  656.  
  657.   PROCEDURE THelpViewer.ChangeBounds(VAR Bounds : TRect);
  658.   BEGIN
  659.     TScroller.ChangeBounds(Bounds);
  660.     Topic^.SetWidth(Size.X);
  661.     SetLimit(Limit.X, Topic^.NumLines);
  662.   END;
  663.  
  664.   PROCEDURE THelpViewer.Draw;
  665.   VAR
  666.     B : TDrawBuffer;
  667.     Line : STRING;
  668.     I, J, L : INTEGER;
  669.     KeyCount : INTEGER;
  670.     Normal, Keyword, SelKeyword, C : BYTE;
  671.     KeyPoint : TPoint;
  672.     KeyLength : BYTE;
  673.     KeyRef : INTEGER;
  674.   BEGIN
  675.     Normal := GetColor(1);
  676.     Keyword := GetColor(2);
  677.     SelKeyword := GetColor(3);
  678.     KeyCount := 0;
  679.     KeyPoint.X := 0;
  680.     KeyPoint.Y := 0;
  681.     Topic^.SetWidth(Size.X);
  682.     IF Topic^.GetNumCrossRefs > 0 THEN
  683.       REPEAT
  684.         INC(KeyCount);
  685.         Topic^.GetCrossRef(KeyCount, KeyPoint, KeyLength, KeyRef);
  686.       UNTIL (KeyCount >= Topic^.GetNumCrossRefs) OR(KeyPoint.Y > Delta.Y);
  687.     FOR I := 1 TO Size.Y DO
  688.       BEGIN
  689.         MoveChar(B, ' ', Normal, Size.X);
  690.         Line := Topic^.GetLine(I+Delta.Y);
  691.         MoveStr(B, COPY(Line, Delta.X+1, Size.X), Normal);
  692.         WHILE I+Delta.Y = KeyPoint.Y DO
  693.           BEGIN
  694.             L := KeyLength;
  695.             IF KeyPoint.X < Delta.X THEN
  696.               BEGIN
  697.                 DEC(L, Delta.X-KeyPoint.X);
  698.                 KeyPoint.X := Delta.X;
  699.               END;
  700.             IF KeyCount = Selected THEN C := SelKeyword
  701.             ELSE C := Keyword;
  702.             FOR J := 0 TO L-1 DO
  703.               WordRec(B[KeyPoint.X-Delta.X+J]).HI := C;
  704.             INC(KeyCount);
  705.             IF KeyCount <= Topic^.GetNumCrossRefs THEN
  706.               Topic^.GetCrossRef(KeyCount, KeyPoint, KeyLength, KeyRef)
  707.             ELSE KeyPoint.Y := 0;
  708.           END;
  709.         WriteLine(0, I-1, Size.X, 1, B);
  710.       END;
  711.   END;
  712.  
  713.   FUNCTION THelpViewer.GetPalette : PPalette;
  714.   CONST
  715.     P : STRING[LENGTH(CHelpViewer)] = CHelpViewer;
  716.   BEGIN
  717.     GetPalette := @P;
  718.   END;
  719.  
  720.   PROCEDURE THelpViewer.HandleEvent(VAR Event : TEvent);
  721.   VAR
  722.     KeyPoint, Mouse : TPoint;
  723.     KeyLength : BYTE;
  724.     KeyRef : INTEGER;
  725.     KeyCount : INTEGER;
  726.  
  727.     PROCEDURE MakeSelectVisible;
  728.     VAR
  729.       D : TPoint;
  730.     BEGIN
  731.       Topic^.GetCrossRef(Selected, KeyPoint, KeyLength, KeyRef);
  732.       D := Delta;
  733.       IF KeyPoint.X < D.X THEN D.X := KeyPoint.X;
  734.       IF KeyPoint.X > D.X+Size.X THEN D.X := KeyPoint.X-Size.X;
  735.       IF KeyPoint.Y < D.Y THEN D.Y := KeyPoint.Y;
  736.       IF KeyPoint.Y > D.Y+Size.Y THEN D.Y := KeyPoint.Y-Size.Y;
  737.       IF (D.X <> Delta.X) OR(D.Y <> Delta.Y) THEN ScrollTo(D.X, D.Y);
  738.     END;
  739.  
  740.     PROCEDURE SwitchToTopic(KeyRef : INTEGER);
  741.     BEGIN
  742.       IF Topic <> NIL THEN DISPOSE(Topic, Done);
  743.       Topic := HFile^.GetTopic(KeyRef);
  744.       Topic^.SetWidth(Size.X);
  745.       ScrollTo(0, 0);
  746.       SetLimit(Limit.X, Topic^.NumLines);
  747.       Selected := 1;
  748.       DrawView;
  749.     END;
  750.  
  751.   BEGIN
  752.     TScroller.HandleEvent(Event);
  753.     CASE Event.What OF
  754.       evKeyDown :
  755.         BEGIN
  756.           CASE Event.KeyCode OF
  757.             kbTab :
  758.               BEGIN
  759.                 INC(Selected);
  760.                 IF Selected > Topic^.GetNumCrossRefs THEN Selected := 1;
  761.                 MakeSelectVisible;
  762.               END;
  763.             kbShiftTab :
  764.               BEGIN
  765.                 DEC(Selected);
  766.                 IF Selected = 0 THEN Selected := Topic^.GetNumCrossRefs;
  767.                 MakeSelectVisible;
  768.               END;
  769.             kbEnter :
  770.               BEGIN
  771.                 IF Selected <= Topic^.GetNumCrossRefs THEN
  772.                   BEGIN
  773.                     Topic^.GetCrossRef(Selected, KeyPoint, KeyLength, KeyRef);
  774.                     SwitchToTopic(KeyRef);
  775.                   END;
  776.               END;
  777.             kbEsc :
  778.               BEGIN
  779.                 Event.What := evCommand;
  780.                 Event.Command := cmClose;
  781.                 PutEvent(Event);
  782.               END;
  783.           ELSE
  784.             EXIT;
  785.           END;
  786.           DrawView;
  787.           ClearEvent(Event);
  788.         END;
  789.       evMouseDown :
  790.         BEGIN
  791.           MakeLocal(Event.Where, Mouse);
  792.           INC(Mouse.X, Delta.X); INC(Mouse.Y, Delta.Y);
  793.           KeyCount := 0;
  794.           REPEAT
  795.             INC(KeyCount);
  796.             IF KeyCount > Topic^.GetNumCrossRefs THEN EXIT;
  797.             Topic^.GetCrossRef(KeyCount, KeyPoint, KeyLength, KeyRef);
  798.           UNTIL (KeyPoint.Y = Mouse.Y+1) AND(Mouse.X >= KeyPoint.X) AND
  799.           (Mouse.X < KeyPoint.X+KeyLength);
  800.           Selected := KeyCount;
  801.           DrawView;
  802.           IF Event.DOUBLE THEN SwitchToTopic(KeyRef);
  803.           ClearEvent(Event);
  804.         END;
  805.       evCommand :
  806.         IF (Event.Command = cmClose) AND(Owner^.State AND sfModal <> 0) THEN
  807.           BEGIN
  808.             EndModal(cmClose);
  809.             ClearEvent(Event);
  810.           END;
  811.     END;
  812.   END;
  813.  
  814.   { THelpWindow }
  815.  
  816.   CONSTRUCTOR THelpWindow.Init(HFile : PHelpFile; Context : WORD);
  817.   VAR
  818.     R : TRect;
  819.   BEGIN
  820.     R.ASSIGN(0, 0, 50, 18);
  821.     TWindow.Init(R, 'Help', wnNoNumber);
  822.     Options := Options OR ofCentered;
  823.     R.Grow(-2, -1);
  824.     INSERT(NEW(PHelpViewer, Init(R,
  825.                                  StandardScrollBar(sbHorizontal+sbHandleKeyboard),
  826.                                  StandardScrollBar(sbVertical+sbHandleKeyboard), HFile, Context)));
  827.   END;
  828.  
  829.   FUNCTION THelpWindow.GetPalette : PPalette;
  830.   CONST
  831.     P : STRING[LENGTH(CHelpWindow)] = CHelpWindow;
  832.   BEGIN
  833.     GetPalette := @P;
  834.   END;
  835.  
  836.   PROCEDURE RegisterHelpFile;
  837.   BEGIN
  838.     RegisterType(RHelpTopic);
  839.     RegisterType(RHelpIndex);
  840.   END;
  841.  
  842.   PROCEDURE NotAssigned(VAR S : TStream; Value : INTEGER);
  843.   BEGIN
  844.   END;
  845.  
  846. END.
  847.