home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_PAS / SCANH326.ZIP / HELPFILE.PAS < prev    next >
Pascal/Delphi Source File  |  1994-01-22  |  16KB  |  585 lines

  1. {$i helpdefs.inc}
  2.  
  3. unit HelpFiles;
  4.  
  5.   { Unit to define abstract help file object.  Included in SCANH3xx.ZIP
  6.     as sample of comment formatting and output. }
  7.  
  8.   {#M}
  9.   { This comment will be highlighted as example text in most output formats.
  10.     The sample below won't be word-wrapped. }
  11.   {#F}
  12.   {  program HelloWorld;  }
  13.   {  begin                }
  14.   {    writeln('Hello, world!'); }
  15.   {  end.                 }
  16.   {#F}
  17.   {#M}
  18.  
  19. interface
  20.  
  21. uses OPString, Objects, Streams, BigCollection, TokenUnit;
  22.  
  23. const
  24. {$ifdef dpmi}
  25.   ForHelpBuffer : TStreamRanking = (RamStream, EMSStream, XMSStream, FileStream);
  26. {$else}
  27.   ForHelpBuffer : TStreamRanking = (EMSStream, XMSStream, FileStream, NoStream);
  28. {$endif}
  29.  
  30. type
  31.   PTopic = ^TTopic;
  32.   TTopic
  33.   = object(TObject)
  34.       { An object holding a single topic as part of a #THelpFile#. }
  35.  
  36.       Text : PStream;
  37.       { A stream to which the text of the topic is written. }
  38.  
  39.       TopicNum : Longint; { The topic number in the help file. }
  40.       StartofLine : Boolean; { Whether the text is currently at the
  41.                              start of a line. }
  42.       FixedLines : Boolean;  { Whether lines should be fixed or wrapped }
  43.       Marked : Boolean;      { Whether text is currently being marked }
  44.       Highlighting : byte;   { Counts the current highlight level }
  45.  
  46.       constructor Init(Atopicnum : Longint);
  47.         { Initialize an empty topic with the given value for #TopicNum#. }
  48.  
  49.       destructor Done; virtual;
  50.         { Dispose of #Text# and destroy object. }
  51.  
  52.       function GetLine(var Buffer; MaxLen : Word) : Word; virtual;
  53.         { Gets the next line of text, return the length }
  54.  
  55.       function MoreLines : Boolean; virtual;
  56.         { True if there are more lines of text. }
  57.  
  58.       procedure Write(s : String); virtual;
  59.         { Writes the string to the help text }
  60.  
  61.       procedure WriteLn(const s : String); virtual;
  62.         { Writes, then adds a newline }
  63.  
  64.       procedure WriteKeyWord(const s : String; Crossref : Longint); virtual;
  65.         { Writes the string with a marker that it's a cross-reference }
  66.  
  67.       procedure HighLight(On : Boolean); virtual;
  68.     { Turns highlighting of the text on or off.  If turned on twice, it will need
  69.       to be turned off twice to return to standard. }
  70.  
  71.       procedure ResetHighLight; virtual;
  72.     { Turns highlighting off regardless of the initial state. }
  73.  
  74.       procedure BlankLine; virtual;
  75.     { Writes a blank line to the help topic, starting a new paragraph
  76.       afterwards. }
  77.  
  78.       procedure StartXrefList(const s : String); virtual;
  79.     { Starts a list of cross-referenced topics. End the list with
  80.       #EndXrefList#. }
  81.  
  82.       procedure WriteXref(const s : String; Len:Word;Crossref : longint); virtual;
  83.     { Like #WriteKeyWord#, but writes an entry to a cross-ref list. Len
  84.       is the length in characters of the longest Xref to come; this may
  85.       be used to format nicely. Assumes that #StartXrefList# has been called. }
  86.  
  87.       procedure EndXrefList; virtual;
  88.     { Ends a list of cross-referenced topics started by #StartXrefList#. }
  89.  
  90.       procedure ToggleFixed; virtual;
  91.     { Toggles word-wrap mode.  Generally, help files start out word-wrapping;
  92.       this should turn it off.  The TTopic method just toggles #FixedLines#. }
  93.  
  94.       procedure ToggleMarked; virtual;
  95.     { Toggles word marking mode.  Typically marked text would be used for
  96.       code samples, as in the Borland help files.  This one just toggles
  97.       #Marked#. }
  98.  
  99.       {$ifdef debug}
  100.       function IntegrityCheck(const msg:string):boolean; virtual;
  101.     { Checks that the object isn't damaged. }
  102.       {$endif}
  103.     end;
  104.  
  105.   PIndexItem = ^TIndexItem;
  106.   TIndexItem = record
  107.       { This is an item stored in the index for a help file. }
  108.  
  109.       Context,
  110.       { The context or topic number. }
  111.       Inserted : longint;
  112.       { The count in the index when this item was inserted; allows
  113.         a stable sort of the index. }
  114.       Subtitle,
  115.       { The token number of the subtitle string. }
  116.       Token : TToken;
  117.       { The token number of the name of index entry. }
  118.     end;
  119.  
  120.   PIndex = ^TIndex;
  121.   TIndex = object(TBigSortedCollection)
  122.       { This is an index for a help file, meant to hold #TIndexItem# records. }
  123.  
  124.       Sortby : (ByToken, BySubTitle, ByContext);
  125.       { Marks which sort order should be used. }
  126.  
  127.       procedure FreeItem(Item : Pointer); virtual;
  128.         { Disposes of a TIndexItem }
  129.  
  130.       function Compare(Item1, Item2 : Pointer) : Integer; virtual;
  131.         { Compares two index items according to the #Sortby# field. }
  132.  
  133.       procedure Insert(Item : Pointer); virtual;
  134.         { This inserts duplicates after existing values. }
  135.  
  136.       procedure AddItem(const ATitle, ASubtitle : String; Atopicnum : Longint);
  137.         { Add a new index entry by specifying the strings to use. }
  138.  
  139.       procedure AddTokens(ATitle, ASubtitle : TToken; Atopicnum : Longint);
  140.         { Add a new index entry by specifying the token numbers. }
  141.  
  142.       {$ifdef debug}
  143.       function IntegrityCheck(const msg:string):boolean; virtual;
  144.     { Checks that the object isn't damaged. }
  145.       {$endif}
  146.     end;
  147.  
  148.   PHelpFile = ^THelpFile;
  149.   THelpFile
  150.   = object(TObject)
  151.   { This is the main abstract object representing a help file.  It serves
  152.     as a container for #THelpTopic#s. }
  153.  
  154.       Index : PIndex;
  155.       { This is a #TIndex# maintained by the help file. }
  156.  
  157.       MultiIndexed : Boolean;
  158.       { Indicates whether a topic can have more than one index entry
  159.         in this help file type. }
  160.  
  161.       constructor Init;
  162.         { Construct an empty help file, and initialize #Index# to nil and
  163.           #MultiIndexed# to false. }
  164.  
  165.       destructor Done; virtual;
  166.         { Destroy the object and dispose of the #Index#. }
  167.  
  168.       function NumTopics : Longint; virtual;
  169.         { Return the number of topics in this file. }
  170.  
  171.       function GetTitle(TopicNum : Longint) : String; virtual;
  172.         { Constructs a topic title for the given topic number. }
  173.  
  174.       function GetSubTitle(TopicNum : Longint) : String; virtual;
  175.         { Constructs a topic subtitle. }
  176.  
  177.       function GetTopic(Context : Longint) : PTopic; virtual;
  178.         { Extracts the given topic from the help file. }
  179.  
  180.       function NewTopic(Context : Longint; Someinfo : Pointer) : PTopic; virtual;
  181.     { Constructs a new topic of the appropriate type. Someinfo might
  182.       be used by a descendant type. }
  183.  
  184.       procedure AddTopic(ATopic : PTopic); virtual;
  185.     { Writes the topic at the end of the base file, and records it with the
  186.       appropriate topic number.  If a topic with that number existed previously,
  187.       it'll effectively be deleted.
  188.       Atopic is disposed after adding it.}
  189.  
  190.       procedure DisplayTopic(var Where : Text; TopicNum : Longint); virtual;
  191.         { Displays the given topic number. }
  192.  
  193.       procedure SetMainTopic(TopicNum : Longint); virtual;
  194.         { Defines which Topic is the main contents topic. }
  195.  
  196.       procedure Rewrite(s : PStream); virtual;
  197.         { Rewrites the help file to the given stream.  }
  198.  
  199.       procedure RewriteNotify(num:longint); virtual;
  200.       { Should be called for each topic as it's written to the output file
  201.         by #ReWrite#. The default version calls #NotifyProc#. This could
  202.         be used to show a status report; num will run from 0 to
  203.         pred(#NumTopics#), possibly skipping some
  204.         values, but doesn't correspond to the topic number.}
  205.  
  206.       function TextSize: Longint; virtual;
  207.         { Returns the total size of text so far }
  208.  
  209.       {$ifdef debug}
  210.       function IntegrityCheck(const msg:string):boolean; virtual;
  211.     { Checks that the object isn't damaged. }
  212.       {$endif}
  213.     end;
  214.  
  215. var
  216.   NotifyProc : procedure(num:Longint);
  217.   { A procedure to be called by #THelpfile.Rewrite#. }
  218.  
  219. {$ifdef debug}
  220. const
  221.   LastEntry : PIndexItem = nil;
  222. {$endif}
  223.  
  224. implementation
  225.  
  226.   constructor TTopic.Init(Atopicnum : Longint);
  227.   begin
  228.     inherited Init;
  229.     TopicNum := Atopicnum;
  230.     StartofLine := True;
  231.   end;
  232.  
  233.   destructor TTopic.Done;
  234.   begin
  235.     if Text <> nil then
  236.       Dispose(Text, Done);
  237.     inherited Done;
  238.   end;
  239.  
  240.   function TTopic.GetLine(var Buffer; MaxLen : Word) : Word;
  241.     { Gets the next line of text, return the length }
  242.   begin
  243.     Abstract;
  244.   end;
  245.  
  246.   function TTopic.MoreLines : Boolean;
  247.     { True if there are more lines of text. }
  248.   begin
  249.     Abstract;
  250.   end;
  251.  
  252.   procedure TTopic.Write(s : String);
  253.     { Writes the string to the help text }
  254.   begin
  255.     if Length(s) > 0 then
  256.     begin
  257.       Text^.Write(s[1], Length(s));
  258.       StartofLine := False;
  259.     end;
  260.   end;
  261.  
  262.   procedure TTopic.WriteLn(const s : String);
  263.     { Writes, then adds a newline }
  264.   const
  265.     CRLF : array[1..2] of Char = ^M^J;
  266.   begin
  267.     Write(s);
  268.     Text^.Write(CRLF, 2);
  269.     StartofLine := True;
  270.   end;
  271.  
  272.   procedure TTopic.WriteKeyWord(const s : String; Crossref : Longint);
  273.     { Writes the string with a marker that it's a cross-reference }
  274.   begin
  275.     Abstract;
  276.   end;
  277.  
  278.   procedure TTopic.HighLight(On : Boolean);
  279.   begin
  280.     if On then
  281.       inc(highlighting)
  282.     else
  283.       dec(highlighting);
  284.   end;
  285.  
  286.   procedure TTopic.ResetHighLight;
  287.   begin
  288.     while highlighting > 0 do
  289.       HighLight(false);
  290.     while highlighting < 0 do
  291.       HighLight(true);
  292.   end;
  293.  
  294.   procedure TTopic.BlankLine;
  295.   begin
  296.     if not StartofLine then
  297.       WriteLn('');
  298.     WriteLn('');
  299.   end;
  300.  
  301.   procedure TTopic.StartXrefList(const s : String);
  302.   begin
  303.     BlankLine;
  304.     HighLight(True);
  305.     WriteLn(s);
  306.     HighLight(False);
  307.     BlankLine;
  308.   end;
  309.  
  310.   procedure TTopic.WriteXref(const s : String; Len:word; Crossref : Longint);
  311.   begin
  312.     WriteKeyWord(s, Crossref);
  313.     WriteLn(Pad('', Len+1-Length(s)));
  314.   end;
  315.  
  316.   procedure TTopic.EndXrefList;
  317.   begin
  318.   end;
  319.  
  320.   procedure TTopic.ToggleFixed;
  321.   begin
  322.     FixedLines := not FixedLines;
  323.   end;
  324.  
  325.   procedure TTopic.ToggleMarked;
  326.   begin
  327.     Marked := not Marked;
  328.   end;
  329.  
  330.   {$ifdef debug}
  331.   function TTopic.IntegrityCheck(const msg:string):boolean;
  332.   begin
  333.     IntegrityCheck := false;
  334.     if text = nil then
  335.       system.writeln(msg,': nil text')
  336.     else if text^.status <> stOK then
  337.       system.writeln(msg,': text status ',text^.status)
  338.     else
  339.       IntegrityCheck := true;
  340.   end;
  341.   {$endif}
  342.  
  343.   procedure TIndex.FreeItem(Item : Pointer);
  344.   begin
  345.     Dispose(PIndexItem(Item));
  346.   end;
  347.  
  348.   function TIndex.Compare(Item1, Item2 : Pointer) : Integer;
  349.   var
  350.     i1 : PIndexItem absolute Item1;
  351.     i2 : PIndexItem absolute Item2;
  352.     s1, s2 : String;
  353.     Result : Integer;
  354.   begin
  355.     case Sortby of
  356.       ByContext :
  357.         begin
  358.           s1 := HexW(i1^.Context);
  359.           s2 := HexW(i2^.Context);
  360.         end;
  361.       ByToken :
  362.         begin
  363.           s1 := Tokens.Num2Pstr(i1^.Token)^+#0+HexW(i1^.Context);
  364.           s2 := Tokens.Num2Pstr(i2^.Token)^+#0+HexW(i2^.Context);
  365.         end;
  366.       BySubTitle : { Sort by context number within subtitle }
  367.         begin
  368.           s1 := Tokens.Num2Pstr(i1^.Subtitle)^+#0+HexW(i1^.Context);
  369.           s2 := Tokens.Num2Pstr(i2^.Subtitle)^+#0+HexW(i2^.Context);
  370.         end;
  371.     end;
  372.     Result := ord(CompUCString(s1, s2)) - 1;
  373.     if (Result = 0) and BreakTies then
  374.       if i1^.Inserted < i2^.Inserted then
  375.         Result := -1
  376.       else
  377.         Result := 1;
  378.     Compare := Result;
  379.   end;
  380.  
  381.   procedure TIndex.Insert(Item : Pointer);
  382.   var
  383.     i : Longint;
  384.     Key : Pointer;
  385.   begin
  386.     Key := KeyOf(Item);
  387.     if Search(Key, i) then
  388.     repeat
  389.       Inc(i);
  390.     until (i >= Count) or (Compare(Key, KeyOf(At(i))) <> 0);
  391.     AtInsert(i, Item);
  392.   end;
  393.  
  394.   procedure TIndex.AddItem(const ATitle, ASubtitle : String; Atopicnum : Longint);
  395.   begin
  396.     if ASubtitle <> '' then
  397.       AddTokens(Tokens.Str2Num(ATitle), Tokens.Str2Num(ASubtitle),
  398.         Atopicnum)
  399.     else
  400.       AddTokens(Tokens.Str2Num(ATitle), NoToken, Atopicnum)
  401.   end;
  402.  
  403.   procedure TIndex.AddTokens(ATitle, ASubtitle : TToken; Atopicnum : Longint);
  404.   var
  405.     Item : PIndexItem;
  406.   begin
  407.     New(Item);
  408.     if Item <> nil then
  409.     begin
  410.       with Item^ do
  411.       begin
  412.         Token := ATitle;
  413.         Context := Atopicnum;
  414.         Subtitle := ASubtitle;
  415.         Inserted := succ(Count);
  416.         {$ifdef debug}
  417.         if (Token < NoToken) or (Token >= Tokens.Count) then
  418.           inline($cc);
  419.         if (Subtitle < NoToken) or (Subtitle >= Tokens.Count) then
  420.           inline($cc);
  421.         {$endif}
  422.       end;
  423.       Insert(Item);
  424.       {$ifdef debug}
  425.       LastEntry := At(Count-1);
  426.       {$endif}
  427.     end;
  428.   end;
  429.  
  430.   {$ifdef debug}
  431.   function TIndex.IntegrityCheck(const msg:string):boolean;
  432.   var
  433.     foundNil : boolean;
  434.  
  435.     Procedure CheckNil(Item:PIndexItem); far;
  436.     begin
  437.       foundNil := foundNil or (Item = Nil);
  438.     end;
  439.  
  440.     function BadItem(Item:PIndexItem):boolean; far;
  441.     begin
  442.       BadItem := true;
  443.       if (Item^.SubTitle < NoToken) or (Item^.Subtitle >= tokens.count) then
  444.         writeln(msg,': bad subtitle on item')
  445.       else if (Item^.Token < NoToken) or (Item^.Token >= tokens.count) then
  446.         writeln(msg,': bad token on item')
  447.       else
  448.         BadItem := false;
  449.     end;
  450.  
  451.   begin
  452.     IntegrityCheck := false;
  453.     foundNil := false;
  454.     ForEach(@CheckNil);
  455.     if FoundNil then
  456.       writeln(msg,':  contains nil items')
  457.     else if FirstThat(@BadItem) <> nil then
  458.       { Message already printed }
  459.     else
  460.       IntegrityCheck := true;
  461.   end;
  462.   {$endif}
  463.  
  464.   constructor THelpFile.Init;
  465.   begin
  466.     inherited Init;
  467.     Index := nil;
  468.   end;
  469.  
  470.   destructor THelpFile.Done;
  471.   begin
  472.     if Index <> nil then
  473.       Dispose(Index, Done);
  474.     inherited Done;
  475.   end;
  476.  
  477.   function THelpFile.NumTopics : Longint;
  478.   begin
  479.     Abstract;
  480.   end;
  481.  
  482.   function THelpFile.GetTitle(TopicNum : Longint) : String;
  483.     { Constructs a topic title }
  484.   var
  485.     i : longint;
  486.     dummyitem : TIndexItem;
  487.   begin
  488.     GetTitle := '';
  489.     if Index^.SortBy = ByContext then
  490.     begin
  491.       dummyitem.context := topicnum;
  492.       if Index^.Search(@dummyitem,i) then
  493.         with PIndexItem(Index^.At(i))^ do
  494.           GetTitle := Tokens.Num2Pstr(Token)^;
  495.     end
  496.     else
  497.       for i := 0 to Pred(Index^.Count) do
  498.         with PIndexItem(Index^.At(i))^ do
  499.           if TopicNum = Context then
  500.           begin
  501.             GetTitle := Tokens.Num2Pstr(Token)^;
  502.             Exit;
  503.           end;
  504.   end;
  505.  
  506.   function THelpFile.GetSubTitle(TopicNum : Longint) : String;
  507.     { Constructs a topic subtitle }
  508.   var
  509.     i : longint;
  510.   begin
  511.     for i := 0 to Pred(Index^.Count) do
  512.       with PIndexItem(Index^.At(i))^ do
  513.         if TopicNum = Context then
  514.         begin
  515.           GetSubTitle := Tokens.Num2Pstr(Subtitle)^;
  516.           Exit;
  517.         end;
  518.     GetSubTitle := '';
  519.   end;
  520.  
  521.   function THelpFile.GetTopic(Context : Longint) : PTopic;
  522.   begin
  523.     Abstract;
  524.   end;
  525.  
  526.   function THelpFile.NewTopic(Context : Longint; Someinfo : Pointer) : PTopic;
  527.   begin
  528.     Abstract;
  529.   end;
  530.  
  531.   procedure THelpFile.AddTopic(ATopic : PTopic);
  532.   begin
  533.     with ATopic^ do
  534.     begin
  535.       if highlighting <> 0 then
  536.         ResetHighlight;
  537.       if FixedLines then
  538.         ToggleFixed;
  539.       if Marked then
  540.         ToggleMarked;
  541.     end;
  542.   end;
  543.  
  544.   procedure THelpFile.DisplayTopic(var Where : Text; TopicNum : Longint);
  545.     { Displays the given topic number }
  546.   begin
  547.     Abstract;
  548.   end;
  549.  
  550.   procedure THelpFile.SetMainTopic(TopicNum : Longint);
  551.   begin
  552.     Abstract;
  553.   end;
  554.  
  555.   procedure THelpFile.Rewrite(s : PStream);
  556.   begin
  557.     Abstract;
  558.   end;
  559.  
  560.   procedure THelpFile.RewriteNotify(num:Longint);
  561.   begin
  562.     if @NotifyProc <> Nil then
  563.       NotifyProc(num);
  564.   end;
  565.  
  566.   function THelpFile.TextSize:longint;
  567.   begin
  568.     Abstract;
  569.   end;
  570.  
  571.   {$ifdef debug}
  572.   function THelpFile.IntegrityCheck(const msg:string):boolean;
  573.   begin
  574.     IntegrityCheck := False;
  575.     if Index = nil then
  576.       writeln(msg,': nil index')
  577.     else
  578.       IntegrityCheck := Index^.IntegrityCheck(msg+':index');
  579.   end;
  580.   {$endif}
  581.  
  582. begin
  583.   @NotifyProc := Nil;
  584. end.
  585.