home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 November / Chip_2003-11_cd1.bin / program / delphi / kompon / DIHtmlLabel.exe / Source / DIHtmlRenderer.pas < prev   
Encoding:
Pascal/Delphi Source File  |  2003-08-27  |  65.6 KB  |  2,168 lines

  1. unit DIHtmlRenderer;
  2.  
  3. {$I DI.inc}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, Graphics,
  9.  
  10.   DIContainers,
  11.   DIObjectVector,
  12.   DIHtmlParser,
  13.   DIHtmlCharSetPlugin,
  14.   DIHtmlMisc;
  15.  
  16. type
  17.  
  18.   { ----------------------------------------------------------------------------
  19.     The Node Tree
  20.  
  21.     Object hierarchy:
  22.  
  23.     TNode
  24.     |  TParentNode
  25.     |  |  TAreaNode
  26.     |  |  |  TStyleNode
  27.     |  |  |  TLinkNode
  28.     |  |  |  TDynamicNode
  29.     |  |  |  TRootNode
  30.     |  TStringNode
  31.     |  TActionNode
  32.   ---------------------------------------------------------------------------- }
  33.  
  34.     { }
  35.   TNodeClass = class of TNode;
  36.  
  37.   { }
  38.   TNodeType = (ntNode, ntParentNode, ntAreaNode, ntFontNode, ntStyleNode, ntLinkNode,
  39.     ntDynamicNode, ntRootNode, ntStringNode, ntActionNode);
  40.  
  41.   TParentNode = class;
  42.  
  43.   { ---------------------------------------------------------------------------- }
  44.  
  45.   { }
  46.   TNode = class(TObject)
  47.   private
  48.     FParent: TParentNode;
  49.   public
  50.     function GetNodeType: TNodeType; virtual;
  51.     property Parent: TParentNode read FParent write FParent;
  52.   end;
  53.  
  54.   { }
  55.   PNode = ^TNode;
  56.  
  57.   { ---------------------------------------------------------------------------- }
  58.  
  59.   { }
  60.   TNodeList = class(TDIObjectVector)
  61.   private
  62.     function Get(const Index: Integer): TNode;
  63.     procedure Put(const Index: Integer; const Value: TNode);
  64.   public
  65.     function Add(const Item: TNode): Integer;
  66.     procedure Insert(const Index: Integer; const Item: TNode);
  67.     function IndexOf(const Item: TNode): Integer;
  68.     property Items[const Index: Integer]: TNode read Get write Put; default;
  69.   end;
  70.  
  71.   { ---------------------------------------------------------------------------- }
  72.  
  73.   { }
  74.   TTopLevelNodeEnumerator = class
  75.   private
  76.     FRootNode: TParentNode;
  77.     FNodeClass: TNodeClass;
  78.  
  79.     FCurrentRoot: TParentNode;
  80.     FCurrentIndex: Integer;
  81.  
  82.     FStack: TDIList;
  83.     FRoot: TParentNode;
  84.     procedure SetRootNode(const ARootNode: TParentNode);
  85.   public
  86.     destructor Destroy; override;
  87.     function NextNode: TNode;
  88.     property NodeClass: TNodeClass read FNodeClass write FNodeClass;
  89.     procedure Reset;
  90.     property RootNode: TParentNode read FRoot write SetRootNode;
  91.   end;
  92.  
  93.   { ---------------------------------------------------------------------------- }
  94.  
  95.   { }
  96.   TParentNode = class(TNode)
  97.   private
  98.     FChildren: TNodeList;
  99.   public
  100.     constructor Create;
  101.     destructor Destroy; override;
  102.     procedure AddChild(const Node: TNode);
  103.     procedure DestroyChildren;
  104.     function GetNodeType: TNodeType; override;
  105.     function IndexOfChild(const Node: TNode): Integer;
  106.     function GetFirstNodeOfClass(const NodeClass: TNodeClass): TNode;
  107.     property Children: TNodeList read FChildren;
  108.   end;
  109.  
  110.   { ---------------------------------------------------------------------------- }
  111.  
  112.   { }
  113. function NewNodeList: TNodeList;
  114. { }
  115. function NewNodeOwnerList: TNodeList;
  116.  
  117. { ---------------------------------------------------------------------------- }
  118.  
  119. type
  120.   { }
  121.   TAreaNode = class(TParentNode)
  122.   private
  123.     FStartingPoint: TPoint;
  124.     FStyles: TFontStyles;
  125.     FColor: TColor;
  126.     function GetText: WideString;
  127.   protected
  128.     function GetStyles: TFontStyles; virtual;
  129.     function GetColor: TColor; virtual;
  130.   public
  131.     constructor Create;
  132.     function GetNodeType: TNodeType; override;
  133.     function IsPointInNode(const p: TPoint): Boolean;
  134.     function IsPointInNodeClass(const p: TPoint; const ANodeClass: TNodeClass): Boolean; virtual;
  135.     function GetNodeAtPointOfClass(const p: TPoint; const NodeClass: TNodeClass): TNode;
  136.     property StartingPoint: TPoint read FStartingPoint write FStartingPoint;
  137.     property Styles: TFontStyles read GetStyles write FStyles;
  138.     property Color: TColor read GetColor write FColor;
  139.     property Text: WideString read GetText;
  140.   end;
  141.  
  142.   { ---------------------------------------------------------------------------- }
  143.   { TFontNode
  144.   { ---------------------------------------------------------------------------- }
  145.  
  146.   { }
  147.   TFontNode = class(TAreaNode)
  148.   public
  149.     function GetNodeType: TNodeType; override;
  150.   end;
  151.  
  152.   { ---------------------------------------------------------------------------- }
  153.   { TStyleNode
  154.   { ---------------------------------------------------------------------------- }
  155.  
  156.   { }
  157.   TStyleNode = class(TAreaNode)
  158.   private
  159.     FStyle: TFontStyle;
  160.   public
  161.     constructor Create(const Style: TFontStyle);
  162.     function GetNodeType: TNodeType; override;
  163.     property Style: TFontStyle read FStyle write FStyle;
  164.   end;
  165.  
  166.   { ---------------------------------------------------------------------------- }
  167.  
  168.   { }
  169.   TLinkState = (lsNormal, lsClicked, lsHot);
  170.  
  171.   { }
  172.   TLinkNode = class(TAreaNode)
  173.   private
  174.     FState: TLinkState;
  175.     FHref: WideString;
  176.   protected
  177.     function GetColor: TColor; override;
  178.   public
  179.     constructor Create(const AHref: WideString);
  180.     function GetNodeType: TNodeType; override;
  181.     property State: TLinkState read FState write FState;
  182.     property Href: WideString read FHref;
  183.   end;
  184.  
  185.   { ---------------------------------------------------------------------------- }
  186.  
  187.   { }
  188.   TDynamicNode = class(TAreaNode)
  189.   private
  190.     FID: WideString;
  191.   public
  192.     constructor Create(const AID: WideString);
  193.     function GetNodeType: TNodeType; override;
  194.     property ID: WideString read FID;
  195.   end;
  196.  
  197.   { ---------------------------------------------------------------------------- }
  198.   { TRectVector
  199.   { ---------------------------------------------------------------------------- }
  200.  
  201.   { }
  202.   TRectVector = class(TDIVector)
  203.   public
  204.     procedure InsertRectLast(const ARect: TRect);
  205.   end;
  206.  
  207.   { ---------------------------------------------------------------------------- }
  208.   { TRootNode
  209.   { ---------------------------------------------------------------------------- }
  210.  
  211.   { }
  212.   TRootNode = class(TAreaNode)
  213.   private
  214.     FRectArray: TRectVector;
  215.   public
  216.     constructor Create;
  217.     destructor Destroy; override;
  218.     function GetNodeType: TNodeType; override;
  219.     procedure RetrieveRectsOfTLinkNodeChildren;
  220.     function IsPointInNodeClass(const p: TPoint; const NodeClass: TNodeClass): Boolean; override;
  221.   end;
  222.  
  223.   { ---------------------------------------------------------------------------- }
  224.  
  225.   { }
  226.   TSpaceInfo = packed record
  227.     LastWordEndsWithSpace: Boolean;
  228.     SpaceWidth: Integer;
  229.   end;
  230.  
  231.   { ---------------------------------------------------------------------------- }
  232.  
  233.   { }
  234.   TWordInfo = packed record
  235.     SpaceInfo: TSpaceInfo;
  236.     Width: Integer;
  237.   end;
  238.   { }
  239.   PWordInfo = ^TWordInfo;
  240.  
  241.   { ---------------------------------------------------------------------------- }
  242.  
  243.   { }
  244.   TStringNode = class(TNode)
  245.   private
  246.     FText: WideString;
  247.     FRectArray: TRectVector;
  248.     FWordInfoArray: TDIVector;
  249.     FFirstWordWidthRetrieved: Boolean;
  250.   protected
  251.   public
  252.     constructor Create(const aText: WideString);
  253.     destructor Destroy; override;
  254.     procedure AddWordInfo(const SpaceInfo: TSpaceInfo; const Width: Integer);
  255.     procedure ClearWordInfo;
  256.     function GetNodeType: TNodeType; override;
  257.     function GetWordInfo(const Index: Integer): TWordInfo;
  258.     function IsPointInNode(const p: TPoint): Boolean;
  259.     property Text: WideString read FText write FText;
  260.     property RectArray: TRectVector read FRectArray;
  261.     property FirstWordWidthRetrieved: Boolean read FFirstWordWidthRetrieved write FFirstWordWidthRetrieved;
  262.     property WordInfos: TDIVector read FWordInfoArray;
  263.   end;
  264.  
  265.   { ---------------------------------------------------------------------------- }
  266.  
  267.   { }
  268.   TActionType = (atLineBreak, atParagraphBreak);
  269.  
  270.   { }
  271.   TActionNode = class(TNode)
  272.   private
  273.     FAction: TActionType;
  274.   public
  275.     constructor Create(const Action: TActionType);
  276.     function GetNodeType: TNodeType; override;
  277.     property Action: TActionType read FAction write FAction;
  278.   end;
  279.  
  280.   { ---------------------------------------------------------------------------- }
  281.  
  282.   { }
  283.   TNodeTree = class(TObject)
  284.   private
  285.     FRoot: TRootNode;
  286.   public
  287.     constructor Create;
  288.     destructor Destroy; override;
  289.     function GetDynamicTagText(const DynamicTagID: WideString): WideString;
  290.     function GetNodeAtPointOfClass(const p: TPoint; NodeClass: TNodeClass): TNode;
  291.     function IsPointInTree(const p: TPoint): Boolean;
  292.     function IsPointInNodeClass(const p: TPoint; NodeClass: TNodeClass): Boolean;
  293.     procedure Clear;
  294.     procedure ClearWordInfos;
  295.     property Root: TRootNode read FRoot;
  296.   end;
  297.  
  298. const
  299.   { }
  300.   clNormalLink = TColor($400 or $80000000);
  301.   { }
  302.   clClickedLink = TColor($401 or $80000000);
  303.   { }
  304.   clHotLink = TColor($402 or $80000000);
  305.  
  306. type
  307.   { ---------------------------------------------------------------------------- }
  308.   { TNodeObserverList
  309.   { ---------------------------------------------------------------------------- }
  310.  
  311.   { }
  312.   PNodeObserver = ^TNodeObserver;
  313.   TNodeObserver = record
  314.     ParentNode: TAreaNode;
  315.     FirstStringNode: TStringNode;
  316.   end;
  317.  
  318.   { ---------------------------------------------------------------------------- }
  319.  
  320.   { }
  321.   TNodeObserverList = class(TDIVector)
  322.   private
  323.     function GetObserver(const Index: Integer): PNodeObserver;
  324.     procedure SetObserver(const Index: Integer; const Value: PNodeObserver);
  325.   public
  326.     procedure AddObserver(const AParentNode: TAreaNode; const AFirstStringNode: TStringNode);
  327.     function IndexOfStringNode(const Node: TStringNode): Integer;
  328.     property Observers[const Index: Integer]: PNodeObserver read GetObserver write SetObserver; default;
  329.   end;
  330.  
  331. function NewNodeObserverList: TNodeObserverList;
  332.  
  333. { ---------------------------------------------------------------------------- }
  334.  
  335. type
  336.   { }
  337.   TElementVector = class;
  338.  
  339.   { ---------------------------------------------------------------------------- }
  340.  
  341.   { }
  342.   TTextHandler = class(TObject)
  343.   private
  344.     FPosX: Integer;
  345.     FPosY: Integer;
  346.     FOffsetY: Integer;
  347.     FElementVector: TElementVector;
  348.     FRect: TRect;
  349.     FCanvas: TCanvas;
  350.     FLineHeight: Integer;
  351.     FObservers: TNodeObserverList;
  352.   public
  353.     constructor Create(const Canvas: TCanvas; const Rect: TRect; const InitialX, InitialY, OffsetY: Integer);
  354.     destructor Destroy; override;
  355.     procedure TextOut(const Node: TStringNode; const Style: TFontStyles; const Color: TColor);
  356.     procedure DoParagraphBreak;
  357.     procedure DoLineBreak;
  358.     procedure EmptyBuffer;
  359.     function GetTextHeight: Integer;
  360.     function IsPosCurrent: Boolean;
  361.     procedure AddStartingPosObserver(const Node: TAreaNode);
  362.     property PosX: Integer read FPosX;
  363.     property PosY: Integer read FPosY;
  364.   end;
  365.  
  366.   { ---------------------------------------------------------------------------- }
  367.   { TParentTextElement
  368.   { ---------------------------------------------------------------------------- }
  369.  
  370.   { }
  371.   TParentTextElement = class
  372.   end;
  373.   { }
  374.   PParentTextElement = ^TParentTextElement;
  375.  
  376.   { ---------------------------------------------------------------------------- }
  377.   { TStringElement
  378.   { ---------------------------------------------------------------------------- }
  379.  
  380.   { }
  381.   TStringElement = class(TParentTextElement)
  382.   private
  383.     FNode: TStringNode;
  384.     FStyle: TFontStyles;
  385.     FColor: TColor;
  386.   public
  387.     constructor Create(const Node: TStringNode; const Style: TFontStyles; const Color: TColor);
  388.     function BeginsWithSpace: Boolean;
  389.     function EndsWithSpace: Boolean;
  390.     property Node: TStringNode read FNode;
  391.     property Style: TFontStyles read FStyle;
  392.     property Color: TColor read FColor;
  393.   end;
  394.  
  395.   { ---------------------------------------------------------------------------- }
  396.   { TActionElement
  397.   { ---------------------------------------------------------------------------- }
  398.  
  399.   { }
  400.   TActionElement = class(TParentTextElement)
  401.   private
  402.     FActionType: TActionType;
  403.   public
  404.     constructor Create(const ActionType: TActionType);
  405.     property ActionType: TActionType read FActionType;
  406.   end;
  407.  
  408.   { ---------------------------------------------------------------------------- }
  409.   { TElementVector
  410.   { ---------------------------------------------------------------------------- }
  411.  
  412.   { }
  413.   TElementVector = class(TDIVector)
  414.   private
  415.     function GetElement(const Index: Integer): TParentTextElement;
  416.   public
  417.     procedure AddStringElement(const Node: TStringNode; const Style: TFontStyles; const Color: TColor);
  418.     procedure AddParagraphBreak;
  419.     procedure AddLineBreak;
  420.     property Items[const Index: Integer]: TParentTextElement read GetElement; default;
  421.   end;
  422.  
  423. function NewElementVector: TElementVector;
  424.  
  425. type
  426.  
  427.   { ---------------------------------------------------------------------------- }
  428.   { IDynamicNodeHandler
  429.   { ---------------------------------------------------------------------------- }
  430.  
  431.     { }
  432.   IDynamicNodeHandler = interface
  433.     procedure HandleDynamicNode(const Node: TDynamicNode; out Source: AnsiString);
  434.   end;
  435.  
  436.   { ---------------------------------------------------------------------------- }
  437.   { TDefaultParser
  438.   { ---------------------------------------------------------------------------- }
  439.  
  440.   { }
  441.   TDefaultParser = class
  442.   private
  443.     FDynamicNodeHandler: IDynamicNodeHandler;
  444.     procedure ParseNode(const HtmlParser: TDIHtmlParser; const Node: TParentNode; var FirstTextInLine: Boolean);
  445.   protected
  446.     procedure HandleDynamicTag(const Node: TDynamicNode);
  447.   public
  448.     function Parse(const Text: AnsiString): TNodeTree; overload;
  449.     procedure AddSourceTreeToDynamicNode(const Node: TDynamicNode; const Source: AnsiString);
  450.     property DynamicNodeHandler: IDynamicNodeHandler read FDynamicNodeHandler write FDynamicNodeHandler;
  451.   end;
  452.  
  453.   { ---------------------------------------------------------------------------- }
  454.   { TDIHtmlRenderer
  455.   { ---------------------------------------------------------------------------- }
  456.  
  457. type
  458.   { }
  459.   TDIHtmlRenderer = class(TObject)
  460.   private
  461.     FLinkColor: TColor;
  462.     FLinkColorClicked: TColor;
  463.     FLinkColorHot: TColor;
  464.     FLinkStyle: TFontStyles;
  465.     FTextHandler: TTextHandler;
  466.     FTextHeight: Integer;
  467.     FTextOffsetY: Integer;
  468.   protected
  469.     procedure DoRenderNode(const Node: TAreaNode; const Styles: TFontStyles; const Color: TColor); virtual;
  470.     function TranslateColor(const Color: TColor): TColor;
  471.   public
  472.     procedure RenderTree(const Canvas: TCanvas; Rect: TRect; const Tree: TNodeTree);
  473.     procedure RenderNode(const Canvas: TCanvas; const Rect: TRect; const Node: TAreaNode);
  474.     property TextHeight: Integer read FTextHeight;
  475.     property LinkColor: TColor read FLinkColor write FLinkColor;
  476.     property LinkColorClicked: TColor read FLinkColorClicked write FLinkColorClicked;
  477.     property LinkColorHot: TColor read FLinkColorHot write FLinkColorHot;
  478.     property LinkStyle: TFontStyles read FLinkStyle write FLinkStyle;
  479.     property TextOffsetY: Integer read FTextOffsetY write FTextOffsetY;
  480.   end;
  481.  
  482. const
  483.   TAG_DYNAMIC = 'DYNAMIC';
  484.   TAG_DYNAMIC_ID = MAX_TAG_ID + 1313;
  485.  
  486. implementation
  487.  
  488. uses
  489.   SysUtils,
  490.   Classes,
  491.  
  492.   DIObjectItemHandler,
  493.   DIObjectOwnerItemHandler,
  494.   DIHtmlColors,
  495.   DIUtils;
  496.  
  497. { ---------------------------------------------------------------------------- }
  498. { TDefaultParser
  499. { ---------------------------------------------------------------------------- }
  500.  
  501. procedure TDefaultParser.AddSourceTreeToDynamicNode(const Node: TDynamicNode; const Source: AnsiString);
  502. var
  503.   HtmlParser: TDIHtmlParser;
  504.   HtmlCharSetPlugin: TDIHtmlCharSetPlugin;
  505.   FirstTextInLine: Boolean;
  506. begin
  507.   HtmlParser := TDIHtmlParser.Create{$IFNDEF DI_No_Unicode_Component}(nil){$ENDIF};
  508.   HtmlCharSetPlugin := TDIHtmlCharSetPlugin.Create{$IFNDEF DI_No_Unicode_Component}(nil){$ENDIF};
  509.   try
  510.     HtmlParser.NormalizeWhiteSpace := True;
  511.     HtmlParser.FilterHtmlTags.SetStartEnd(fiShow);
  512.     HtmlParser.FilterText := fiShow;
  513.     HtmlParser.SourceBufferAsStrA := Source;
  514.  
  515.     HtmlCharSetPlugin.HtmlParser := HtmlParser;
  516.     Node.DestroyChildren;
  517.     FirstTextInLine := False; // No guarantee it's the first line
  518.     ParseNode(HtmlParser, Node, FirstTextInLine);
  519.   finally
  520.     HtmlCharSetPlugin.Free;
  521.     HtmlParser.Free;
  522.   end;
  523. end;
  524.  
  525. { ---------------------------------------------------------------------------- }
  526.  
  527. procedure TDefaultParser.HandleDynamicTag(const Node: TDynamicNode);
  528. var
  529.   Source: AnsiString;
  530. begin
  531.   if Assigned(FDynamicNodeHandler) then
  532.     begin
  533.       FDynamicNodeHandler.HandleDynamicNode(Node, Source);
  534.       if Pointer(Source) <> nil then AddSourceTreeToDynamicNode(Node, Source);
  535.     end;
  536. end;
  537.  
  538. { ---------------------------------------------------------------------------- }
  539.  
  540. function TDefaultParser.Parse(const Text: AnsiString): TNodeTree;
  541. var
  542.   HtmlParser: TDIHtmlParser;
  543.   HtmlCharSetPlugin: TDIHtmlCharSetPlugin;
  544.   FirstTextInLine: Boolean;
  545. begin
  546.   Result := TNodeTree.Create;
  547.  
  548.   HtmlParser := TDIHtmlParser.Create{$IFNDEF DI_No_Unicode_Component}(nil){$ENDIF};
  549.   HtmlCharSetPlugin := TDIHtmlCharSetPlugin.Create{$IFNDEF DI_No_Unicode_Component}(nil){$ENDIF};
  550.   try
  551.     HtmlParser.NormalizeWhiteSpace := True;
  552.     HtmlParser.FilterHtmlTags.SetStartEnd(fiShow);
  553.     HtmlParser.FilterText := fiShow;
  554.     HtmlParser.SourceBufferAsStrA := Text;
  555.  
  556.     HtmlCharSetPlugin.HtmlParser := HtmlParser;
  557.     FirstTextInLine := True;
  558.     ParseNode(HtmlParser, Result.Root, FirstTextInLine);
  559.   finally
  560.     HtmlCharSetPlugin.Free;
  561.     HtmlParser.Free;
  562.   end;
  563. end;
  564.  
  565. { ---------------------------------------------------------------------------- }
  566.  
  567. procedure TDefaultParser.ParseNode(const HtmlParser: TDIHtmlParser; const Node: TParentNode; var FirstTextInLine: Boolean);
  568. var
  569.   NewNode: TNode;
  570.   w: WideString;
  571. begin
  572.   while HtmlParser.ParseNextPiece do
  573.     begin
  574.       NewNode := nil;
  575.  
  576.       case HtmlParser.PieceType of
  577.  
  578.         ptHtmlTag:
  579.           case HtmlParser.HtmlTag.TagType of
  580.  
  581.             ttStartTag:
  582.               case HtmlParser.HtmlTag.TagID of
  583.  
  584.                 TAG_A_ID:
  585.                   NewNode := TLinkNode.Create(HtmlParser.HtmlTag.ValueOfNumber[ATTRIB_HREF_ID, 0]);
  586.  
  587.                 TAG_B_ID:
  588.                   NewNode := TStyleNode.Create(fsBold);
  589.  
  590.                 TAG_BR_ID:
  591.                   begin
  592.                     NewNode := TActionNode.Create(atLineBreak);
  593.                     FirstTextInLine := True;
  594.                   end;
  595.  
  596.                 TAG_DYNAMIC_ID:
  597.                   begin
  598.                     NewNode := TDynamicNode.Create(HtmlParser.HtmlTag.ValueOfNumber[ATTRIB_HREF_ID, 0]);
  599.                     HandleDynamicTag(NewNode as TDynamicNode);
  600.                   end;
  601.  
  602.                 TAG_FONT_ID:
  603.                   begin
  604.                     w := HtmlParser.HtmlTag.ValueOfNumber[ATTRIB_COLOR_ID, 0];
  605.                     if Pointer(w) <> nil then
  606.                       begin
  607.                         NewNode := TFontNode.Create;
  608.                         TFontNode(NewNode).Color := ColorFromHtml(w);
  609.                       end
  610.                     else
  611.                       Continue;
  612.                   end;
  613.  
  614.                 TAG_I_ID:
  615.                   NewNode := TStyleNode.Create(fsItalic);
  616.  
  617.                 TAG_P_ID:
  618.                   begin
  619.                     NewNode := TActionNode.Create(atParagraphBreak);
  620.                     FirstTextInLine := True;
  621.                   end;
  622.  
  623.                 TAG_S_ID:
  624.                   NewNode := TStyleNode.Create(fsStrikeOut);
  625.  
  626.                 TAG_U_ID:
  627.                   NewNode := TStyleNode.Create(fsUnderline);
  628.  
  629.               else
  630.                 Continue;
  631.               end;
  632.  
  633.             ttEndTag:
  634.               begin
  635.                 case HtmlParser.HtmlTag.TagID of
  636.  
  637.                   TAG_A_ID:
  638.                     if Node is TLinkNode then
  639.                       Break;
  640.  
  641.                   TAG_B_ID:
  642.                     if (Node is TStyleNode) and (TStyleNode(Node).Style = fsBold) then
  643.                       Break;
  644.  
  645.                   TAG_FONT_ID:
  646.                     if Node is TFontNode then
  647.                       Break;
  648.  
  649.                   TAG_I_ID:
  650.                     if (Node is TStyleNode) and (TStyleNode(Node).Style = fsItalic) then
  651.                       Break;
  652.  
  653.                   TAG_S_ID:
  654.                     if (Node is TStyleNode) and (TStyleNode(Node).Style = fsStrikeOut) then
  655.                       Break;
  656.  
  657.                   TAG_U_ID:
  658.                     if (Node is TStyleNode) and (TStyleNode(Node).Style = fsUnderline) then
  659.                       Break;
  660.  
  661.                 end;
  662.                 { Ignore all unknown closing tags and continue the parsing
  663.                   at this node level. }
  664.                 Continue;
  665.               end;
  666.  
  667.           end;
  668.  
  669.         ptText:
  670.           if not FirstTextInLine then
  671.             begin
  672.               NewNode := TStringNode.Create(HtmlParser.DataAsStrW);
  673.             end
  674.           else
  675.             begin
  676.               NewNode := TStringNode.Create(HtmlParser.DataAsStrTrimLeftW);
  677.               FirstTextInLine := False;
  678.             end;
  679.  
  680.       else
  681.         Continue;
  682.       end;
  683.  
  684.       if NewNode <> nil then
  685.         begin
  686.           Node.AddChild(NewNode);
  687.           { Returns whether the given node can contain other elements and thus
  688.             descends from TParentNode. Descendants from this class begin with <?> and
  689.             end with </?> (for example, <B> and </B>). Nodes that descend from
  690.             TActionNode shouldn't be terminated with </?> (for example, <P>). Note
  691.             that TDynamicNode is special; while it descends from TParentNode, it never
  692.             contains children at parse-time, thus we shouldn't wait for a redundant
  693.             </DYNAMIC>. Instead, its contents are supplied before it's rendered by
  694.             compiled program code. }
  695.           if (HtmlParser.HtmlTag.TagType = ttStartTag) and
  696.             (NewNode is TParentNode) and
  697.             not (NewNode is TDynamicNode) then
  698.             ParseNode(HtmlParser, TParentNode(NewNode), firsttextinline);
  699.         end;
  700.     end;
  701. end;
  702.  
  703. { ---------------------------------------------------------------------------- }
  704. { TRectVector
  705. { ---------------------------------------------------------------------------- }
  706.  
  707. var
  708.   RectItemHandler: TDIItemHandler = nil;
  709.  
  710. function GetRectItemHandler: TDIItemHandler;
  711. begin
  712.   if RectItemHandler = nil then
  713.     begin
  714.       RectItemHandler := TDIItemHandler.Create;
  715.       RectItemHandler.ItemSize := SizeOf(TRect);
  716.     end;
  717.   Result := RectItemHandler;
  718. end;
  719.  
  720. { ---------------------------------------------------------------------------- }
  721.  
  722. function NewRectVector: TRectVector;
  723. begin
  724.   Result := TRectVector.Create(GetRectItemHandler);
  725. end;
  726.  
  727. { ---------------------------------------------------------------------------- }
  728.  
  729. function SamePointInRectFunc(const Sender: TDIContainer; const PItem1, PItem2, Extra: Pointer): Boolean;
  730. begin
  731.   Result := PtInRect(PRect(PItem1)^, PPoint(PItem2)^);
  732. end;
  733.  
  734. { ---------------------------------------------------------------------------- }
  735.  
  736. procedure TRectVector.InsertRectLast(const ARect: TRect);
  737. begin
  738.   with PRect(InsertItemLast)^ do
  739.     begin
  740.       Left := ARect.Left;
  741.       Top := ARect.Top;
  742.       Right := ARect.Right;
  743.       Bottom := ARect.Bottom;
  744.     end;
  745. end;
  746.  
  747. { ---------------------------------------------------------------------------- }
  748. { TNodeTree
  749. { ---------------------------------------------------------------------------- }
  750.  
  751. constructor TNodeTree.Create;
  752. begin
  753.   inherited;
  754.   FRoot := TRootNode.Create;
  755.   FRoot.Color := clWindowText;
  756. end;
  757.  
  758. { ---------------------------------------------------------------------------- }
  759.  
  760. destructor TNodeTree.Destroy;
  761. begin
  762.   Clear;
  763.   FRoot.Free;
  764.   inherited;
  765. end;
  766.  
  767. { ---------------------------------------------------------------------------- }
  768.  
  769. procedure TNodeTree.Clear;
  770. begin
  771.   FRoot.DestroyChildren;
  772.   inherited;
  773. end;
  774.  
  775. { ---------------------------------------------------------------------------- }
  776.  
  777. procedure TNodeTree.ClearWordInfos;
  778. var
  779.   NodeEnum: TTopLevelNodeEnumerator;
  780.   StringNode: TStringNode;
  781. begin
  782.   NodeEnum := TTopLevelNodeEnumerator.Create;
  783.   NodeEnum.RootNode := FRoot;
  784.   NodeEnum.NodeClass := TStringNode;
  785.  
  786.   StringNode := TStringNode(NodeEnum.NextNode);
  787.   while StringNode <> nil do
  788.     begin
  789.       StringNode.ClearWordInfo;
  790.       StringNode := TStringNode(NodeEnum.NextNode);
  791.     end;
  792.   NodeEnum.Free;
  793. end;
  794.  
  795. { ---------------------------------------------------------------------------- }
  796.  
  797. function TNodeTree.GetNodeAtPointOfClass(const p: TPoint; NodeClass: TNodeClass): TNode;
  798. begin
  799.   Result := FRoot.GetNodeAtPointOfClass(p, NodeClass);
  800. end;
  801.  
  802. { ---------------------------------------------------------------------------- }
  803.  
  804. function TNodeTree.IsPointInNodeClass(const p: TPoint; NodeClass: TNodeClass): Boolean;
  805. begin
  806.   Result := FRoot.IsPointInNodeClass(p, NodeClass);
  807. end;
  808.  
  809. { ---------------------------------------------------------------------------- }
  810.  
  811. function TNodeTree.IsPointInTree(const p: TPoint): Boolean;
  812. begin
  813.   Result := FRoot.IsPointInNode(p);
  814. end;
  815.  
  816. { ---------------------------------------------------------------------------- }
  817. { TParentNode
  818. { ---------------------------------------------------------------------------- }
  819.  
  820. constructor TParentNode.Create;
  821. begin
  822.   inherited;
  823.   FChildren := NewNodeOwnerList;
  824. end;
  825.  
  826. { ---------------------------------------------------------------------------- }
  827.  
  828. destructor TParentNode.Destroy;
  829. begin
  830.   FChildren.Free;
  831.   inherited;
  832. end;
  833.  
  834. { ---------------------------------------------------------------------------- }
  835.  
  836. procedure TParentNode.AddChild(const Node: TNode);
  837. begin
  838.   FChildren.InsertObjectLast(Node);
  839.   Node.Parent := Self;
  840. end;
  841.  
  842. { ---------------------------------------------------------------------------- }
  843.  
  844. procedure TParentNode.DestroyChildren;
  845. begin
  846.   FChildren.Clear;
  847. end;
  848.  
  849. { ---------------------------------------------------------------------------- }
  850.  
  851. function TParentNode.GetFirstNodeOfClass(const NodeClass: TNodeClass): TNode;
  852.  
  853.   function RecurseTree(const CurrentRoot: TParentNode): TNode;
  854.   var
  855.     i: Integer;
  856.   begin
  857.     Result := nil;
  858.     for i := 0 to CurrentRoot.Children.Count - 1 do
  859.       if CurrentRoot.Children[i] is NodeClass then
  860.         begin
  861.           Result := CurrentRoot.FChildren[i];
  862.           Exit;
  863.         end
  864.       else
  865.         if CurrentRoot.Children[i] is TParentNode then
  866.           Result := RecurseTree(TParentNode(CurrentRoot.Children[i]));
  867.   end;
  868.  
  869. begin
  870.   Result := RecurseTree(Self);
  871. end;
  872.  
  873. { ---------------------------------------------------------------------------- }
  874.  
  875. function TParentNode.GetNodeType: TNodeType;
  876. begin
  877.   Result := ntParentNode;
  878. end;
  879.  
  880. { ---------------------------------------------------------------------------- }
  881.  
  882. function TParentNode.IndexOfChild(const Node: TNode): Integer;
  883. begin
  884.   Result := FChildren.IndexOf(Node);
  885. end;
  886.  
  887. { ---------------------------------------------------------------------------- }
  888. { TNodeList
  889. { ---------------------------------------------------------------------------- }
  890.  
  891. function NewNodeList: TNodeList;
  892. begin
  893.   Result := TNodeList.Create(GetDIObjectItemHandler);
  894. end;
  895.  
  896. { ---------------------------------------------------------------------------- }
  897.  
  898. function NewNodeOwnerList: TNodeList;
  899. begin
  900.   Result := TNodeList.Create(GetDIObjectOwnerItemHandler);
  901. end;
  902.  
  903. { ---------------------------------------------------------------------------- }
  904.  
  905. function TNodeList.Add(const Item: TNode): Integer;
  906. begin
  907.   Result := Count;
  908.   InsertObjectLast(Item);
  909. end;
  910.  
  911. { ---------------------------------------------------------------------------- }
  912.  
  913. function TNodeList.Get(const Index: Integer): TNode;
  914. begin
  915.   Result := TNode(ObjectAt[Index]);
  916. end;
  917.  
  918. { ---------------------------------------------------------------------------- }
  919.  
  920. function TNodeList.IndexOf(const Item: TNode): Integer;
  921. begin
  922.   Result := IndexOfObject(Item);
  923. end;
  924.  
  925. { ---------------------------------------------------------------------------- }
  926.  
  927. procedure TNodeList.Insert(const Index: Integer; const Item: TNode);
  928. begin
  929.   InsertObjectAt(Index, Item);
  930. end;
  931.  
  932. { ---------------------------------------------------------------------------- }
  933.  
  934. procedure TNodeList.Put(const Index: Integer; const Value: TNode);
  935. begin
  936.   ObjectAt[Index] := Value;
  937. end;
  938.  
  939. { ---------------------------------------------------------------------------- }
  940. { TStringNode
  941. { ---------------------------------------------------------------------------- }
  942.  
  943. var
  944.   WordInfoItemHandler: TDIItemHandler = nil;
  945.  
  946. function GetWordInfoItemHandler: TDIItemHandler;
  947. begin
  948.   Result := WordInfoItemHandler;
  949.   if Result = nil then
  950.     begin
  951.       WordInfoItemHandler := TDIItemHandler.Create;
  952.       with WordInfoItemHandler do
  953.         begin
  954.           ItemSize := SizeOf(TWordInfo);
  955.         end;
  956.       Result := WordInfoItemHandler;
  957.     end;
  958. end;
  959.  
  960. { ---------------------------------------------------------------------------- }
  961.  
  962. constructor TStringNode.Create(const aText: WideString);
  963. begin
  964.   inherited Create;
  965.   FWordInfoArray := TDIVector.Create(GetWordInfoItemHandler);
  966.   FRectArray := NewRectVector;
  967.   FText := aText;
  968. end;
  969.  
  970. { ---------------------------------------------------------------------------- }
  971.  
  972. destructor TStringNode.Destroy;
  973. begin
  974.   FRectArray.Free;
  975.   FWordInfoArray.Free;
  976.   inherited;
  977. end;
  978.  
  979. { ---------------------------------------------------------------------------- }
  980.  
  981. procedure TStringNode.AddWordInfo(const SpaceInfo: TSpaceInfo; const Width: Integer);
  982. var
  983.   WordInfo: PWordInfo;
  984. begin
  985.   WordInfo := FWordInfoArray.InsertItemLast;
  986.   WordInfo^.SpaceInfo := SpaceInfo;
  987.   WordInfo^.Width := Width;
  988. end;
  989.  
  990. { ---------------------------------------------------------------------------- }
  991.  
  992. procedure TStringNode.ClearWordInfo;
  993. begin
  994.   FWordInfoArray.Clear;
  995. end;
  996.  
  997. { ---------------------------------------------------------------------------- }
  998.  
  999. function TStringNode.GetNodeType: TNodeType;
  1000. begin
  1001.   Result := ntStringNode;
  1002. end;
  1003.  
  1004. { ---------------------------------------------------------------------------- }
  1005.  
  1006. function TStringNode.GetWordInfo(const Index: Integer): TWordInfo;
  1007. begin
  1008.   Result := PWordInfo(FWordInfoArray.PItemAt[Index])^;
  1009. end;
  1010.  
  1011. { ---------------------------------------------------------------------------- }
  1012.  
  1013. function TStringNode.IsPointInNode(const p: TPoint): Boolean;
  1014. begin
  1015.   Result := FRectArray.Exists(@p, SamePointInRectFunc);
  1016. end;
  1017.  
  1018. { ---------------------------------------------------------------------------- }
  1019. { TStyleNode
  1020. { ---------------------------------------------------------------------------- }
  1021.  
  1022. constructor TStyleNode.Create(const Style: TFontStyle);
  1023. begin
  1024.   inherited Create;
  1025.   FStyle := Style;
  1026. end;
  1027.  
  1028. { ---------------------------------------------------------------------------- }
  1029.  
  1030. function TStyleNode.GetNodeType: TNodeType;
  1031. begin
  1032.   Result := ntStyleNode;
  1033. end;
  1034.  
  1035. { ---------------------------------------------------------------------------- }
  1036. { TActionNode
  1037. { ---------------------------------------------------------------------------- }
  1038.  
  1039. constructor TActionNode.Create(const Action: TActionType);
  1040. begin
  1041.   inherited Create;
  1042.   FAction := Action;
  1043. end;
  1044.  
  1045. { ---------------------------------------------------------------------------- }
  1046.  
  1047. function TActionNode.GetNodeType: TNodeType;
  1048. begin
  1049.   Result := ntActionNode;
  1050. end;
  1051.  
  1052. { ---------------------------------------------------------------------------- }
  1053. { TAreaNode
  1054. { ---------------------------------------------------------------------------- }
  1055.  
  1056. constructor TAreaNode.Create;
  1057. var
  1058.   Zero: Integer;
  1059. begin
  1060.   inherited;
  1061.   Zero := 0;
  1062.   with FStartingPoint do
  1063.     begin
  1064.       x := Zero;
  1065.       y := Zero;
  1066.     end;
  1067. end;
  1068.  
  1069. { ---------------------------------------------------------------------------- }
  1070.  
  1071. function TAreaNode.GetColor: TColor;
  1072. begin
  1073.   Result := FColor;
  1074. end;
  1075.  
  1076. { ---------------------------------------------------------------------------- }
  1077.  
  1078. function TAreaNode.GetNodeAtPointOfClass(const p: TPoint; const NodeClass: TNodeClass): TNode;
  1079. var
  1080.   NodeEnum: TTopLevelNodeEnumerator;
  1081. begin
  1082.   NodeEnum := TTopLevelNodeEnumerator.Create;
  1083.   NodeEnum.RootNode := Self;
  1084.   NodeEnum.NodeClass := TAreaNode;
  1085.   Result := NodeEnum.NextNode;
  1086.   while Result <> nil do
  1087.     begin
  1088.       if TAreaNode(Result).IsPointInNode(p) then
  1089.         if Result is NodeClass then
  1090.           Break
  1091.         else
  1092.           begin
  1093.             Result := TAreaNode(Result).GetNodeAtPointOfClass(p, NodeClass);
  1094.             if Result <> nil then Break;
  1095.           end;
  1096.       Result := NodeEnum.NextNode;
  1097.     end;
  1098.   NodeEnum.Free;
  1099. end;
  1100.  
  1101. { ---------------------------------------------------------------------------- }
  1102.  
  1103. function TAreaNode.GetNodeType: TNodeType;
  1104. begin
  1105.   Result := ntAreaNode;
  1106. end;
  1107.  
  1108. { ---------------------------------------------------------------------------- }
  1109.  
  1110. function TAreaNode.GetStyles: TFontStyles;
  1111. begin
  1112.   Result := FStyles;
  1113. end;
  1114.  
  1115. { ---------------------------------------------------------------------------- }
  1116.  
  1117. function TAreaNode.GetText: WideString;
  1118. var
  1119.   NodeEnum: TTopLevelNodeEnumerator;
  1120.   Node: TStringNode;
  1121. begin
  1122.   Result := '';
  1123.   NodeEnum := TTopLevelNodeEnumerator.Create;
  1124.   NodeEnum.RootNode := Self;
  1125.   NodeEnum.NodeClass := TStringNode;
  1126.   Node := TStringNode(NodeEnum.NextNode);
  1127.   while Node <> nil do
  1128.     begin
  1129.       Result := Result + Node.Text;
  1130.       Node := TStringNode(NodeEnum.NextNode);
  1131.     end;
  1132.   NodeEnum.Free;
  1133. end;
  1134.  
  1135. { ---------------------------------------------------------------------------- }
  1136.  
  1137. function TAreaNode.IsPointInNode(const p: TPoint): Boolean;
  1138. var
  1139.   NodeEnum: TTopLevelNodeEnumerator;
  1140.   Node: TStringNode;
  1141. begin
  1142.   NodeEnum := TTopLevelNodeEnumerator.Create;
  1143.   NodeEnum.RootNode := Self;
  1144.   NodeEnum.NodeClass := TStringNode;
  1145.   Node := TStringNode(NodeEnum.NextNode);
  1146.   while (Node <> nil) and not Node.IsPointInNode(p) do
  1147.     Node := TStringNode(NodeEnum.NextNode);
  1148.   NodeEnum.Free;
  1149.   Result := Node <> nil;
  1150. end;
  1151.  
  1152. { ---------------------------------------------------------------------------- }
  1153.  
  1154. function TAreaNode.IsPointInNodeClass(const p: TPoint; const ANodeClass: TNodeClass): Boolean;
  1155. var
  1156.   NodeEnum: TTopLevelNodeEnumerator;
  1157.   Node: TNode;
  1158. begin
  1159.   Result := False;
  1160.   NodeEnum := TTopLevelNodeEnumerator.Create;
  1161.   NodeEnum.RootNode := Self;
  1162.   NodeEnum.NodeClass := ANodeClass;
  1163.  
  1164.   Node := NodeEnum.NextNode;
  1165.   while Node <> nil do
  1166.     begin
  1167.       if Node is TAreaNode then
  1168.         begin
  1169.           Result := TAreaNode(Node).IsPointInNode(p);
  1170.           if Result then Break;
  1171.         end;
  1172.       Node := NodeEnum.NextNode;
  1173.     end;
  1174.   NodeEnum.Free;
  1175. end;
  1176.  
  1177. { ---------------------------------------------------------------------------- }
  1178. { TNode
  1179. { ---------------------------------------------------------------------------- }
  1180.  
  1181. function TNode.GetNodeType: TNodeType;
  1182. begin
  1183.   { We get the dynamic type using TObject.ClassType, which returns a pointer to
  1184.     the class' virtual memory table, instead of testing using the "is" reserved
  1185.     word. We do this as any node is a TNode (thanks to polymorphism); we would
  1186.     have to test in reverse order, as if we tested for TNode first everything
  1187.     would appear to be a TNode. This could get messy when we add more TNode
  1188.     descendants later. }
  1189.   Result := ntNode;
  1190. end;
  1191.  
  1192. { ---------------------------------------------------------------------------- }
  1193. { TTopLevelNodeEnumerator
  1194. { ---------------------------------------------------------------------------- }
  1195.  
  1196. var
  1197.   NodeStackItemHandler: TDIItemHandler = nil;
  1198.  
  1199. type
  1200.   TNodeStackItem = packed record
  1201.     Node: TParentNode;
  1202.     Index: Integer;
  1203.   end;
  1204.   PNodeStackItem = ^TNodeStackItem;
  1205.  
  1206. function GetNodeStackItemHandler: TDIItemHandler;
  1207. begin
  1208.   if NodeStackItemHandler = nil then
  1209.     begin
  1210.       NodeStackItemHandler := TDIItemHandler.Create;
  1211.       NodeStackItemHandler.ItemSize := SizeOf(TNodeStackItem);
  1212.     end;
  1213.   Result := NodeStackItemHandler;
  1214. end;
  1215.  
  1216. { ---------------------------------------------------------------------------- }
  1217.  
  1218. destructor TTopLevelNodeEnumerator.Destroy;
  1219. begin
  1220.   FStack.Free;
  1221.   inherited;
  1222. end;
  1223.  
  1224. { ---------------------------------------------------------------------------- }
  1225.  
  1226. function TTopLevelNodeEnumerator.NextNode: TNode;
  1227. label
  1228.   Start;
  1229. var
  1230.   NodeStackItem: PNodeStackItem;
  1231. begin
  1232.   Start:
  1233.   if FCurrentIndex < FCurrentRoot.Children.Count then
  1234.     begin
  1235.       Result := FCurrentRoot.Children[FCurrentIndex];
  1236.       Inc(FCurrentIndex);
  1237.       { If we find a child that is of the requested type, return it. Do not add
  1238.         it to the stack, as we're not interested in this node's children.
  1239.         After all, we are a top level enumerator! }
  1240.       if Result is FNodeClass then
  1241.         Exit
  1242.       else
  1243.         begin
  1244.           if Result is TParentNode then
  1245.             begin
  1246.               if FStack = nil then
  1247.                 FStack := TDIList.Create(GetNodeStackItemHandler);
  1248.               NodeStackItem := FStack.InsertItemLast;
  1249.               NodeStackItem^.Node := FCurrentRoot;
  1250.               NodeStackItem^.Index := FCurrentIndex;
  1251.  
  1252.               FCurrentRoot := TParentNode(Result);
  1253.               FCurrentIndex := 0;
  1254.             end;
  1255.           goto Start;
  1256.         end
  1257.     end
  1258.   else
  1259.     if (FStack <> nil) and (FStack.IsNotEmpty) then
  1260.       begin
  1261.         NodeStackItem := FStack.PLastItem;
  1262.         FCurrentRoot := NodeStackItem^.Node;
  1263.         FCurrentIndex := NodeStackItem^.Index;
  1264.         FStack.DeleteLast;
  1265.         goto Start;
  1266.       end;
  1267.   Result := nil;
  1268. end;
  1269.  
  1270. { ---------------------------------------------------------------------------- }
  1271.  
  1272. procedure TTopLevelNodeEnumerator.Reset;
  1273. begin
  1274.   if FStack <> nil then
  1275.     FStack.Clear;
  1276.   FCurrentRoot := FRootNode;
  1277.   FCurrentIndex := 0;
  1278. end;
  1279.  
  1280. { ---------------------------------------------------------------------------- }
  1281.  
  1282. procedure TTopLevelNodeEnumerator.SetRootNode(const ARootNode: TParentNode);
  1283. begin
  1284.   FRootNode := ARootNode;
  1285.   Reset;
  1286. end;
  1287.  
  1288. { ---------------------------------------------------------------------------- }
  1289. { TFontNode
  1290. { ---------------------------------------------------------------------------- }
  1291.  
  1292. function TFontNode.GetNodeType: TNodeType;
  1293. begin
  1294.   Result := ntFontNode;
  1295. end;
  1296.  
  1297. { ---------------------------------------------------------------------------- }
  1298. { TLinkNode }
  1299. { ---------------------------------------------------------------------------- }
  1300.  
  1301. constructor TLinkNode.Create(const AHref: WideString);
  1302. begin
  1303.   inherited Create;
  1304.   FHref := AHref;
  1305. end;
  1306.  
  1307. { ---------------------------------------------------------------------------- }
  1308.  
  1309. function TLinkNode.GetColor: TColor;
  1310. begin
  1311.   case State of
  1312.     lsNormal:
  1313.       Result := clNormalLink;
  1314.     lsClicked:
  1315.       Result := clClickedLink;
  1316.     lsHot:
  1317.       Result := clHotLink;
  1318.   else
  1319.     Result := FColor;
  1320.   end;
  1321. end;
  1322.  
  1323. { ---------------------------------------------------------------------------- }
  1324.  
  1325. function TLinkNode.GetNodeType: TNodeType;
  1326. begin
  1327.   Result := ntLinkNode;
  1328. end;
  1329.  
  1330. { ---------------------------------------------------------------------------- }
  1331. { TRootNode }
  1332. { ---------------------------------------------------------------------------- }
  1333.  
  1334. constructor TRootNode.Create;
  1335. begin
  1336.   inherited Create;
  1337.   FRectArray := NewRectVector;
  1338. end;
  1339.  
  1340. { ---------------------------------------------------------------------------- }
  1341.  
  1342. destructor TRootNode.Destroy;
  1343. begin
  1344.   FRectArray.Free;
  1345.   inherited;
  1346. end;
  1347.  
  1348. { ---------------------------------------------------------------------------- }
  1349.  
  1350. function TRootNode.GetNodeType: TNodeType;
  1351. begin
  1352.   Result := ntRootNode;
  1353. end;
  1354.  
  1355. { ---------------------------------------------------------------------------- }
  1356.  
  1357. function TRootNode.IsPointInNodeClass(const p: TPoint; const NodeClass: TNodeClass): Boolean;
  1358. begin
  1359.   { In the root, we cache the locations of all our TLinkNode children, not only
  1360.     our most immediate children but all of them, even if they have a parent
  1361.     other than the root node. We do this to improve performance, as this routine
  1362.     might be queried every time the mouse is moved. On a PII-400 MHz computer,
  1363.     TDIHtmlLabel alone might consume 20% CPU power without this optimization when
  1364.     we move the mouse pointer as fast as we can, which is not acceptable. With
  1365.     this optimization, we consume only about a third as much CPU power. }
  1366.   if NodeClass = TLinkNode then
  1367.     Result := FRectArray.Exists(@p, SamePointInRectFunc)
  1368.   else
  1369.     Result := inherited IsPointInNodeClass(p, NodeClass);
  1370. end;
  1371.  
  1372. { ---------------------------------------------------------------------------- }
  1373.  
  1374. procedure TRootNode.RetrieveRectsOfTLinkNodeChildren;
  1375. var
  1376.   LinkNodeEnum, StringNodeEnum: TTopLevelNodeEnumerator;
  1377.   LinkNode: TLinkNode;
  1378.   StringNode: TStringNode;
  1379.   i: Integer;
  1380. begin
  1381.   FRectArray.Clear;
  1382.   LinkNodeEnum := TTopLevelNodeEnumerator.Create;
  1383.   LinkNodeEnum.RootNode := Self;
  1384.   LinkNodeEnum.NodeClass := TLinkNode;
  1385.  
  1386.   StringNodeEnum := TTopLevelNodeEnumerator.Create;
  1387.   StringNodeEnum.NodeClass := TStringNode;
  1388.  
  1389.   LinkNode := TLinkNode(LinkNodeEnum.NextNode);
  1390.   while LinkNode <> nil do
  1391.     begin
  1392.       StringNodeEnum.RootNode := LinkNode;
  1393.       StringNode := TStringNode(StringNodeEnum.NextNode);
  1394.       while StringNode <> nil do
  1395.         begin
  1396.           for i := 0 to StringNode.RectArray.Count - 1 do
  1397.             FRectArray.InsertRectLast(PRect(StringNode.RectArray.PItemAt[i])^);
  1398.           StringNode := TStringNode(StringNodeEnum.NextNode);
  1399.         end;
  1400.       LinkNode := TLinkNode(LinkNodeEnum.NextNode);
  1401.     end;
  1402.  
  1403.   StringNodeEnum.Free;
  1404.   LinkNodeEnum.Free;
  1405. end;
  1406.  
  1407. { ---------------------------------------------------------------------------- }
  1408. { TDynamicNode
  1409. { ---------------------------------------------------------------------------- }
  1410.  
  1411. constructor TDynamicNode.Create(const AID: WideString);
  1412. begin
  1413.   inherited Create;
  1414.   FID := AID;
  1415. end;
  1416.  
  1417. { ---------------------------------------------------------------------------- }
  1418.  
  1419. function TNodeTree.GetDynamicTagText(const DynamicTagID: WideString): WideString;
  1420. var
  1421.   NodeEnum: TTopLevelNodeEnumerator;
  1422.   Node: TDynamicNode;
  1423. begin
  1424.   NodeEnum := TTopLevelNodeEnumerator.Create;
  1425.   NodeEnum.RootNode := Root;
  1426.   NodeEnum.NodeClass := TDynamicNode;
  1427.  
  1428.   Node := TDynamicNode(NodeEnum.NextNode);
  1429.   while (Node <> nil) and (Node.ID <> DynamicTagID) do
  1430.     Node := TDynamicNode(NodeEnum.NextNode);
  1431.   NodeEnum.Free;
  1432.  
  1433.   if Node <> nil then
  1434.     Result := Node.Text
  1435.   else
  1436.     Result := '';
  1437. end;
  1438.  
  1439. { ---------------------------------------------------------------------------- }
  1440.  
  1441. function TDynamicNode.GetNodeType: TNodeType;
  1442. begin
  1443.   Result := ntDynamicNode;
  1444. end;
  1445.  
  1446. { ---------------------------------------------------------------------------- }
  1447. { TWordEnumerator
  1448. { ---------------------------------------------------------------------------- }
  1449.  
  1450. type
  1451.   TWordEnumerator = class(TObject)
  1452.   private
  1453.     FTextPtr: PWideChar;
  1454.     FTextLength: Cardinal;
  1455.     FText: WideString;
  1456.     FCount: Integer;
  1457.     procedure SetText(const aText: WideString);
  1458.     function GetNext(const IncrementPos: Boolean): WideString;
  1459.   public
  1460.     function PeekNext: WideString;
  1461.     function PopNext: WideString;
  1462.     function HasNext: Boolean;
  1463.     procedure Reset;
  1464.  
  1465.     property Count: Integer read FCount;
  1466.     property Text: WideString read FText write SetText;
  1467.   end;
  1468.  
  1469.   { ---------------------------------------------------------------------------- }
  1470.   { TElementVector
  1471.   { ---------------------------------------------------------------------------- }
  1472.  
  1473. function NewElementVector: TElementVector;
  1474. begin
  1475.   Result := TElementVector.Create(GetDIObjectOwnerItemHandler);
  1476. end;
  1477.  
  1478. { ---------------------------------------------------------------------------- }
  1479.  
  1480. procedure TElementVector.AddLineBreak;
  1481. begin
  1482.   PParentTextElement(InsertItemLast)^ := TActionElement.Create(atLineBreak);
  1483. end;
  1484.  
  1485. { ---------------------------------------------------------------------------- }
  1486.  
  1487. procedure TElementVector.AddParagraphBreak;
  1488. begin
  1489.   PParentTextElement(InsertItemLast)^ := TActionElement.Create(atParagraphBreak);
  1490. end;
  1491.  
  1492. { ---------------------------------------------------------------------------- }
  1493.  
  1494. procedure TElementVector.AddStringElement(const Node: TStringNode; const Style: TFontStyles; const Color: TColor);
  1495. begin
  1496.   PParentTextElement(InsertItemLast)^ := TStringElement.Create(Node, Style, Color);
  1497. end;
  1498.  
  1499. { ---------------------------------------------------------------------------- }
  1500.  
  1501. function TElementVector.GetElement(const Index: Integer): TParentTextElement;
  1502. begin
  1503.   Result := PParentTextElement(PItemAt[Index])^;
  1504. end;
  1505.  
  1506. { ---------------------------------------------------------------------------- }
  1507. { TTextHandler
  1508. { ---------------------------------------------------------------------------- }
  1509.  
  1510. procedure TTextHandler.AddStartingPosObserver(const Node: TAreaNode);
  1511. begin
  1512.   FObservers.AddObserver(Node, TStringNode(Node.GetFirstNodeOfClass(TStringNode)));
  1513. end;
  1514.  
  1515. { ---------------------------------------------------------------------------- }
  1516.  
  1517. constructor TTextHandler.Create(const Canvas: TCanvas; const Rect: TRect; const InitialX, InitialY, OffsetY: Integer);
  1518. var
  1519.   TempFontStyle: TFontStyles;
  1520. const
  1521.   MaximumHeightString = 'fg';
  1522. begin
  1523.   inherited Create;
  1524.   FCanvas := Canvas;
  1525.   FRect := Rect;
  1526.   FPosX := InitialX;
  1527.   FPosY := InitialY;
  1528.   FOffsetY := OffsetY;
  1529.  
  1530.   { TextHeight returns slightly different values depending on whether fsBold is
  1531.     in Canvas.Font.Style. This is not acceptable, as it's important that
  1532.     FLineHeight stays constant between TTextHandler instances. Thus we set
  1533.     Canvas.Font.Style to [] before calculating the line height. }
  1534.   TempFontStyle := Canvas.Font.Style;
  1535.   Canvas.Font.Style := [];
  1536.   FLineHeight := TextHeightW(Canvas.Handle, MaximumHeightString);
  1537.   Canvas.Font.Style := TempFontStyle;
  1538.  
  1539.   FElementVector := NewElementVector;
  1540.   FObservers := NewNodeObserverList;
  1541. end;
  1542.  
  1543. { ---------------------------------------------------------------------------- }
  1544.  
  1545. destructor TTextHandler.Destroy;
  1546. begin
  1547.   FObservers.Free;
  1548.   FElementVector.Free;
  1549.   inherited;
  1550. end;
  1551.  
  1552. { ---------------------------------------------------------------------------- }
  1553.  
  1554. procedure TTextHandler.DoLineBreak;
  1555. begin
  1556.   FElementVector.AddLineBreak;
  1557.   EmptyBuffer;
  1558. end;
  1559.  
  1560. { ---------------------------------------------------------------------------- }
  1561.  
  1562. procedure TTextHandler.DoParagraphBreak;
  1563. begin
  1564.   FElementVector.AddParagraphBreak;
  1565.   EmptyBuffer;
  1566. end;
  1567.  
  1568. { ---------------------------------------------------------------------------- }
  1569.  
  1570. procedure TTextHandler.EmptyBuffer;
  1571. var
  1572.   i: Integer;
  1573.   StrElement: TStringElement;
  1574.   WordEnum: TWordEnumerator;
  1575.   Buffer: WideString;
  1576.   NextWord: WideString;
  1577.   NextWordWidth: Integer;
  1578.   Width: Integer;
  1579.   SpaceInfo: TSpaceInfo;
  1580.  
  1581.   function GetWidth(out SpaceInfo: TSpaceInfo): Integer;
  1582.   var
  1583.     j: Integer;
  1584.     PrivateWordEnum: TWordEnumerator;
  1585.     WordElement: WideString;
  1586.     CurrentElement: TStringElement;
  1587.   begin
  1588.     { If the width of the first word has already been included in the count,
  1589.       don't count it again; thus, return 0. }
  1590.     if StrElement.Node.FirstWordWidthRetrieved and (WordEnum.Count = 1) then
  1591.       Result := 0
  1592.     else
  1593.       Result := TextWidthW(FCanvas.Handle, NextWord);
  1594.  
  1595.     { Update record with default information; might be overwritten later if
  1596.       we're dealing with quite special markup. }
  1597.     with SpaceInfo do
  1598.       begin
  1599.         LastWordEndsWithSpace := (Pointer(NextWord) <> nil) and (NextWord[Length(NextWord)] = WC_SPACE);
  1600.         SpaceWidth := TextWidthW(FCanvas.Handle, WC_SPACE);
  1601.       end;
  1602.  
  1603.     if not WordEnum.HasNext and not (StrElement.Node.FirstWordWidthRetrieved and (WordEnum.Count = 1)) then
  1604.       begin
  1605.         PrivateWordEnum := nil;
  1606.  
  1607.         j := i + 1;
  1608.  
  1609.         while (j < FElementVector.Count) and
  1610.           (FElementVector[j - 1] is TStringElement) and
  1611.           (FElementVector[j] is TStringElement) and
  1612.           (not TStringElement(FElementVector[j - 1]).EndsWithSpace) and
  1613.           (not TStringElement(FElementVector[j]).BeginsWithSpace) do // Part of the same word
  1614.           begin
  1615.             CurrentElement := TStringElement(FElementVector[j]);
  1616.  
  1617.             if PrivateWordEnum = nil then
  1618.               PrivateWordEnum := TWordEnumerator.Create;
  1619.             PrivateWordEnum.Text := CurrentElement.Node.Text;
  1620.  
  1621.             FCanvas.Font.Style := CurrentElement.Style;
  1622.             WordElement := PrivateWordEnum.PopNext;
  1623.             Inc(Result, TextWidthW(FCanvas.Handle, WordElement));
  1624.             CurrentElement.Node.FirstWordWidthRetrieved := True;
  1625.  
  1626.             // Update record
  1627.             if j = FElementVector.Count - 1 then
  1628.               with SpaceInfo do
  1629.                 begin
  1630.                   LastWordEndsWithSpace := (Pointer(WordElement) <> nil) and (WordElement[Length(WordElement)] = WC_SPACE);
  1631.                   SpaceWidth := TextWidthW(FCanvas.Handle, WC_SPACE);
  1632.                 end;
  1633.  
  1634.             // We're only φnterested in the first word; let's break if there are more
  1635.             if PrivateWordEnum.HasNext then
  1636.               Break;
  1637.             Inc(j);
  1638.           end;
  1639.  
  1640.         PrivateWordEnum.Free;
  1641.  
  1642.         // Restore canvas
  1643.         FCanvas.Font.Style := StrElement.Style;
  1644.       end;
  1645.   end;
  1646.  
  1647.   { ---------- }
  1648.  
  1649.   function GetWidthWithoutLastSpace: Integer;
  1650.   begin
  1651.     if SpaceInfo.LastWordEndsWithSpace then
  1652.       Result := Width - SpaceInfo.SpaceWidth
  1653.     else
  1654.       Result := Width;
  1655.   end;
  1656.  
  1657.   { ---------- }
  1658.  
  1659.   function IsFirstWordOfSource: Boolean;
  1660.   begin
  1661.     { If we are processing the first word of the source, we don't want to word
  1662.       wrap; we'd simply leave an empty line at the top. }
  1663.     Result := (FPosX = FRect.Left) and (FPosY = FRect.Top) and (WordEnum.Count = 1);
  1664.   end;
  1665.  
  1666.   { ---------- }
  1667.  
  1668.   function IsInWord: Boolean;
  1669.   begin
  1670.     Result := StrElement.Node.FirstWordWidthRetrieved and (WordEnum.Count = 1);
  1671.   end;
  1672.  
  1673.   { ---------- }
  1674.  
  1675.   procedure NotifyObservers;
  1676.   var
  1677.     Index: Integer;
  1678.   begin
  1679.     { Notify observers that we are processing the node they are interested in.
  1680.       Note that more than one observer may be interested in monitoring the same
  1681.       node; TDynamicNode is a good example. }
  1682.     Index := FObservers.IndexOfStringNode(StrElement.Node);
  1683.     while Index >= 0 do
  1684.       begin
  1685.         with FObservers[Index]^.ParentNode.StartingPoint do
  1686.           begin
  1687.             x := FPosX;
  1688.             y := FPosY;
  1689.           end;
  1690.         FObservers.DeleteAt(Index);
  1691.         Index := FObservers.IndexOfStringNode(StrElement.Node);
  1692.       end;
  1693.   end;
  1694.  
  1695.   { ---------- }
  1696.  
  1697.   function GetCurrentRect: TRect;
  1698.   begin
  1699.     Result := Rect(FPosX, FPosY + FOffsetY, FPosX + TextWidthW(FCanvas.Handle, Buffer), FPosY + +FOffsetY + FLineHeight);
  1700.   end;
  1701.  
  1702. var
  1703.   Element: TParentTextElement;
  1704. begin
  1705.   for i := 0 to FElementVector.Count - 1 do
  1706.     begin
  1707.       Element := FElementVector[i];
  1708.  
  1709.       if Element is TActionElement then
  1710.         begin
  1711.           FPosX := FRect.Left;
  1712.           case TActionElement(Element).ActionType of
  1713.             atLineBreak:
  1714.               Inc(FPosY, FLineHeight);
  1715.             atParagraphBreak:
  1716.               // Add half the line height, but at least 2 additional pixel regardless of FLineHeight.
  1717.               Inc(FPosY, FLineHeight + (FLineHeight div 2) or 2);
  1718.           end;
  1719.         end
  1720.       else
  1721.         if Element is TStringElement then
  1722.           with FCanvas do
  1723.             begin
  1724.               StrElement := TStringElement(Element);
  1725.               NotifyObservers;
  1726.  
  1727.               Font.Style := StrElement.Style;
  1728.               Font.Color := StrElement.Color;
  1729.  
  1730.               WordEnum := TWordEnumerator.Create;
  1731.               WordEnum.Text := StrElement.Node.Text;
  1732.               Buffer := '';
  1733.               Width := 0;
  1734.               StrElement.Node.RectArray.Clear;
  1735.  
  1736.               while WordEnum.HasNext do
  1737.                 begin
  1738.                   NextWord := WordEnum.PopNext;
  1739.  
  1740.                   { We cache information about each individual word to speed rendering;
  1741.                     this way, we don't have to recalculate this information every time
  1742.                     this routine is called (basically every time the tree needs to be
  1743.                     repainted). We also do this as we otherwise wouldn't get correct
  1744.                     output when rendering nodes individually (for example, we frequently
  1745.                     rerender TLinkNodes with a different color). We only break after every
  1746.                     complete word, and one node might not contain complete words. GetWidth
  1747.                     makes use of information from other nodes succeeding the current one
  1748.                     if necessary; this explains why it's important to only store
  1749.                     information gathered when rendering the complete tree, that is, the
  1750.                     first time we render anything at all. }
  1751.                   if StrElement.Node.WordInfos.Count >= WordEnum.Count then
  1752.                     begin
  1753.                       NextWordWidth := StrElement.Node.GetWordInfo(WordEnum.Count - 1).Width;
  1754.                       SpaceInfo := StrElement.Node.GetWordInfo(WordEnum.Count - 1).SpaceInfo;
  1755.                     end
  1756.                   else
  1757.                     begin
  1758.                       NextWordWidth := GetWidth(SpaceInfo);
  1759.                       StrElement.Node.AddWordInfo(SpaceInfo, NextWordWidth);
  1760.                     end;
  1761.  
  1762.                   Inc(Width, NextWordWidth);
  1763.  
  1764.                   if (FPosX + GetWidthWithoutLastSpace >= FRect.Right) and
  1765.                     not (NextWord = WC_SPACE) and // Never wrap because of lone space elements
  1766.                   not IsFirstWordOfSource and // Don't wrap if we have yet to output anything
  1767.                   not IsInWord then // We can't wrap if we're in the middle of rendering a word
  1768.                     begin // Word wrap
  1769.                       { Output contents of buffer, empty it and start on a new line, thus
  1770.                         resetting FPosX and incrementing FPosY. }
  1771.                       TrimRightByRefW(Buffer);
  1772.  
  1773.                       { Check to draw text only if it is within out rectangle.
  1774.                         Some graphic output weired things if the painting
  1775.                          coordinates are way out of the drawing rect (> 8000 pixels). }
  1776.                       if (FPosY + FOffsetY + FLineHeight > 0) and (FPosY + FOffsetY < FRect.Bottom) then
  1777.                         ExtTextOutW(Handle, FPosX, FPosY + FOffsetY, 0, nil, Pointer(Buffer), Length(Buffer), nil);
  1778.                       StrElement.Node.RectArray.InsertRectLast(GetCurrentRect);
  1779.                       Buffer := '';
  1780.                       FPosX := FRect.Left;
  1781.                       Width := NextWordWidth;
  1782.                       Inc(FPosY, FLineHeight);
  1783.                     end
  1784.                   else
  1785.                     if StrElement.Node.FirstWordWidthRetrieved and
  1786.                       WordEnum.HasNext and
  1787.                       (WordEnum.Count = 1) then
  1788.                       Inc(Width, TextWidthW(FCanvas.Handle, NextWord));
  1789.  
  1790.                   Buffer := Buffer + NextWord;
  1791.                 end;
  1792.               WordEnum.Free;
  1793.  
  1794.               if (FPosY + FOffsetY + FLineHeight > 0) and (FPosY + FOffsetY < FRect.Bottom) then
  1795.                 ExtTextOutW(Handle, FPosX, FPosY + FOffsetY, 0, nil, Pointer(Buffer), Length(Buffer), nil);
  1796.               StrElement.Node.RectArray.InsertRectLast(GetCurrentRect);
  1797.               Inc(FPosX, TextWidthW(FCanvas.Handle, Buffer));
  1798.             end
  1799.         else
  1800.           raise Exception.Create('TTextHandler.EmptyBuffer: Unsupported TParentTextElement descendant encountered');
  1801.  
  1802.     end;
  1803.   FElementVector.Clear;
  1804. end;
  1805.  
  1806. { ---------------------------------------------------------------------------- }
  1807.  
  1808. function TTextHandler.GetTextHeight: Integer;
  1809. begin
  1810.   Result := FPosY + FLineHeight;
  1811. end;
  1812.  
  1813. { ---------------------------------------------------------------------------- }
  1814.  
  1815. function TTextHandler.IsPosCurrent: Boolean;
  1816. begin
  1817.   Result := FElementVector.Count = 0;
  1818. end;
  1819.  
  1820. { ---------------------------------------------------------------------------- }
  1821.  
  1822. procedure TTextHandler.TextOut(const Node: TStringNode; const Style: TFontStyles; const Color: TColor);
  1823. { Consider these strings:
  1824.   "This is a <B>test</B>"
  1825.     We first store the string and its attributes in our list. As it ends with
  1826.     a space character, we know it's safe to empty our buffer (thus rendering
  1827.     the results to the screen). When we encounter "test", we don't know for
  1828.     sure whether it'll be followed by a new word or a new substring ("run"?).
  1829.     We have to wait until someone tells us that we've reached the end of the
  1830.     string by calling our public EmptyBuffer method.
  1831.   "This is a<B> test</B>"
  1832.     As usual, we store the first node element ("This is a"). As it doesn't end
  1833.     with a space, it could be followed by another character. However, when we
  1834.     encounter " test", we know that it was indeed a separate word. We
  1835.     immediately call EmptyBuffer before parsing the new string.
  1836.   "<B>Te</B><I>s</I>ting stuff "
  1837.     Here's an instance of the general problem this class was designed to
  1838.     solve. We first store "Te" and its attributes, as it might only be a part
  1839.     of a word. Indeed, in this case we're right. When we get to "s", we store
  1840.     this in a second entry in the list. "ting" is then stored in a third
  1841.     entry after which we discover that the last character is a space, meaning
  1842.     that we've assembled an entire word. Thus we empty our buffer. }
  1843. var
  1844.   l: Cardinal;
  1845. begin
  1846.   l := Cardinal(Node.Text);
  1847.   if l = 0 then Exit;
  1848.  
  1849.   if Node.Text[1] = WC_SPACE then
  1850.     EmptyBuffer;
  1851.  
  1852.   FElementVector.AddStringElement(Node, Style, Color);
  1853.  
  1854.   if Node.Text[PCardinal(l - 4)^ div 2] = WC_SPACE then
  1855.     EmptyBuffer;
  1856. end;
  1857.  
  1858. { ---------------------------------------------------------------------------- }
  1859. { TWordEnumerator
  1860. { ---------------------------------------------------------------------------- }
  1861.  
  1862. function TWordEnumerator.GetNext(const IncrementPos: Boolean): WideString;
  1863. var
  1864.   p: PWideChar;
  1865.   l: Cardinal;
  1866. begin
  1867.   l := FTextLength;
  1868.   if l > 0 then
  1869.     begin
  1870.       p := FTextPtr;
  1871.  
  1872.       repeat
  1873.         if p^ = WC_SPACE then
  1874.           Break;
  1875.         Inc(p);
  1876.         Dec(l);
  1877.       until l = 0;
  1878.  
  1879.       if l > 0 then
  1880.         begin
  1881.           Inc(p);
  1882.           Dec(l);
  1883.         end;
  1884.  
  1885.       SetString(Result, FTextPtr, FTextLength - l);
  1886.       Inc(FCount);
  1887.  
  1888.       if IncrementPos then
  1889.         begin
  1890.           FTextPtr := p;
  1891.           FTextLength := l;
  1892.         end;
  1893.  
  1894.     end
  1895.   else
  1896.     raise Exception.Create('TWordEnumerator.GetNext: No more words to return');
  1897. end;
  1898.  
  1899. { ---------------------------------------------------------------------------- }
  1900.  
  1901. function TWordEnumerator.HasNext: Boolean;
  1902. begin
  1903.   Result := FTextLength > 0;
  1904. end;
  1905.  
  1906. { ---------------------------------------------------------------------------- }
  1907.  
  1908. function TWordEnumerator.PeekNext: WideString;
  1909. begin
  1910.   Result := GetNext(False);
  1911. end;
  1912.  
  1913. { ---------------------------------------------------------------------------- }
  1914.  
  1915. function TWordEnumerator.PopNext: WideString;
  1916. begin
  1917.   Result := GetNext(True);
  1918. end;
  1919.  
  1920. { ---------------------------------------------------------------------------- }
  1921.  
  1922. procedure TWordEnumerator.Reset;
  1923. begin
  1924.   FTextPtr := Pointer(FText);
  1925.   FTextLength := Length(FText);
  1926.   FCount := 0;
  1927. end;
  1928.  
  1929. { ---------------------------------------------------------------------------- }
  1930.  
  1931. procedure TWordEnumerator.SetText(const aText: WideString);
  1932. begin
  1933.   FText := aText;
  1934.   Reset;
  1935. end;
  1936.  
  1937. { ---------------------------------------------------------------------------- }
  1938. { TNodeObserverList
  1939. { ---------------------------------------------------------------------------- }
  1940.  
  1941. var
  1942.   NodeObserverItemHandler: TDIItemHandler = nil;
  1943.  
  1944. function GetNodeObserverItemHandler: TDIItemHandler;
  1945. begin
  1946.   Result := NodeObserverItemHandler;
  1947.   if Result = nil then
  1948.     begin
  1949.       NodeObserverItemHandler := TDIItemHandler.Create;
  1950.       with NodeObserverItemHandler do
  1951.         begin
  1952.           ItemSize := SizeOf(TNodeObserver);
  1953.         end;
  1954.       Result := NodeObserverItemHandler;
  1955.     end;
  1956. end;
  1957.  
  1958. { ---------------------------------------------------------------------------- }
  1959.  
  1960. function NewNodeObserverList: TNodeObserverList;
  1961. begin
  1962.   Result := TNodeObserverList.Create(GetNodeObserverItemHandler);
  1963. end;
  1964.  
  1965. { ---------------------------------------------------------------------------- }
  1966.  
  1967. procedure TNodeObserverList.AddObserver(const AParentNode: TAreaNode; const AFirstStringNode: TStringNode);
  1968. begin
  1969.   with PNodeObserver(InsertItemLast)^ do
  1970.     begin
  1971.       ParentNode := AParentNode;
  1972.       FirstStringNode := AFirstStringNode;
  1973.     end;
  1974. end;
  1975.  
  1976. { ---------------------------------------------------------------------------- }
  1977.  
  1978. function TNodeObserverList.GetObserver(const Index: Integer): PNodeObserver;
  1979. begin
  1980.   Result := PNodeObserver(PItemAt[Index]);
  1981. end;
  1982.  
  1983. { ---------------------------------------------------------------------------- }
  1984.  
  1985. function TNodeObserverList.IndexOfStringNode(const Node: TStringNode): Integer;
  1986. var
  1987.   i: Integer;
  1988. begin
  1989.   for i := 0 to Count - 1 do
  1990.     if PNodeObserver(PItemAt[i])^.FirstStringNode = Node then
  1991.       begin
  1992.         Result := i;
  1993.         Exit;
  1994.       end;
  1995.   Result := -1;
  1996. end;
  1997.  
  1998. { ---------------------------------------------------------------------------- }
  1999.  
  2000. procedure TNodeObserverList.SetObserver(const Index: Integer; const Value: PNodeObserver);
  2001. var
  2002.   Item: PNodeObserver;
  2003. begin
  2004.   Item := PItemAt[Index];
  2005.   // Clear the Item, as we are assigning an Interface.
  2006.   ItemHandler.OnInitItem(Self, Item);
  2007.   with PNodeObserver(InsertItemLast)^ do
  2008.     begin
  2009.       // Observer := Value.Observer;
  2010.       ParentNode := Value.ParentNode;
  2011.       FirstStringNode := Value.FirstStringNode;
  2012.     end;
  2013. end;
  2014.  
  2015. { ---------------------------------------------------------------------------- }
  2016. { TStringElement
  2017. { ---------------------------------------------------------------------------- }
  2018.  
  2019. constructor TStringElement.Create(const Node: TStringNode; const Style: TFontStyles; const Color: TColor);
  2020. begin
  2021.   inherited Create;
  2022.   FNode := Node;
  2023.   FStyle := Style;
  2024.   FColor := Color;
  2025. end;
  2026.  
  2027. { ---------------------------------------------------------------------------- }
  2028.  
  2029. function TStringElement.BeginsWithSpace: Boolean;
  2030. begin
  2031.   with FNode do
  2032.     Result := (Pointer(Text) <> nil) and (Text[1] = WC_SPACE);
  2033. end;
  2034.  
  2035. { ---------------------------------------------------------------------------- }
  2036.  
  2037. function TStringElement.EndsWithSpace: Boolean;
  2038. begin
  2039.   with FNode do
  2040.     Result := (Pointer(Text) <> nil) and (Text[Length(Text)] = WC_SPACE);
  2041. end;
  2042.  
  2043. { ---------------------------------------------------------------------------- }
  2044. { TActionElement
  2045. { ---------------------------------------------------------------------------- }
  2046.  
  2047. constructor TActionElement.Create(const ActionType: TActionType);
  2048. begin
  2049.   inherited Create;
  2050.   FActionType := ActionType;
  2051. end;
  2052.  
  2053. { ---------------------------------------------------------------------------- }
  2054. { TDIHtmlRenderer }
  2055. { ---------------------------------------------------------------------------- }
  2056.  
  2057. procedure TDIHtmlRenderer.DoRenderNode(const Node: TAreaNode; const Styles: TFontStyles; const Color: TColor);
  2058. var
  2059.   i: Integer;
  2060.   ChildNode: TNode;
  2061.   NewStyles: TFontStyles;
  2062.   NewColor: TColor;
  2063. begin
  2064.   if FTextHandler.IsPosCurrent then
  2065.     Node.StartingPoint := Point(FTextHandler.PosX, FTextHandler.PosY)
  2066.   else
  2067.     FTextHandler.AddStartingPosObserver(Node);
  2068.  
  2069.   Node.Styles := Styles;
  2070.   Node.Color := Color;
  2071.  
  2072.   for i := 0 to Node.Children.Count - 1 do
  2073.     begin
  2074.       ChildNode := Node.Children[i];
  2075.       NewColor := TranslateColor(Color);
  2076.       NewStyles := Styles;
  2077.  
  2078.       case ChildNode.GetNodeType of
  2079.  
  2080.         ntActionNode:
  2081.           case TActionNode(ChildNode).Action of
  2082.             atLineBreak:
  2083.               FTextHandler.DoLineBreak;
  2084.             atParagraphBreak:
  2085.               FTextHandler.DoParagraphBreak;
  2086.           end;
  2087.  
  2088.         ntFontNode:
  2089.           begin
  2090.             NewColor := TFontNode(ChildNode).Color;
  2091.           end;
  2092.  
  2093.         ntLinkNode:
  2094.           begin
  2095.             NewStyles := Styles + FLinkStyle;
  2096.             NewColor := FLinkColor;
  2097.           end;
  2098.  
  2099.         ntStringNode:
  2100.           FTextHandler.TextOut(TStringNode(ChildNode), NewStyles, NewColor);
  2101.  
  2102.         ntStyleNode:
  2103.           NewStyles := Styles + [TStyleNode(ChildNode).Style];
  2104.  
  2105.       end;
  2106.  
  2107.       if ChildNode is TAreaNode then
  2108.         DoRenderNode(TAreaNode(ChildNode), NewStyles, NewColor);
  2109.     end;
  2110. end;
  2111.  
  2112. { ---------------------------------------------------------------------------- }
  2113.  
  2114. procedure TDIHtmlRenderer.RenderNode(const Canvas: TCanvas; const Rect: TRect; const Node: TAreaNode);
  2115. begin
  2116.   FTextHandler := TTextHandler.Create(Canvas, Rect, Node.StartingPoint.x, Node.StartingPoint.y, FTextOffsetY);
  2117.   try
  2118.     DoRenderNode(Node, Node.Styles, Node.Color);
  2119.     FTextHandler.EmptyBuffer;
  2120.     FTextHeight := FTextHandler.GetTextHeight;
  2121.   finally
  2122.     FTextHandler.Free;
  2123.   end;
  2124. end;
  2125.  
  2126. { ---------------------------------------------------------------------------- }
  2127.  
  2128. procedure TDIHtmlRenderer.RenderTree(const Canvas: TCanvas; Rect: TRect; const Tree: TNodeTree);
  2129. begin
  2130.   Tree.Root.StartingPoint := Point(Rect.Left, Rect.Top);
  2131.   RenderNode(Canvas, Rect, Tree.Root);
  2132.   Tree.Root.RetrieveRectsOfTLinkNodeChildren;
  2133. end;
  2134.  
  2135. { ---------------------------------------------------------------------------- }
  2136.  
  2137. function TDIHtmlRenderer.TranslateColor(const Color: TColor): TColor;
  2138. begin
  2139.   Result := Color;
  2140.   case Result of
  2141.     clNormalLink:
  2142.       Result := FLinkColor;
  2143.     clClickedLink:
  2144.       Result := FLinkColorClicked;
  2145.     clHotLink:
  2146.       Result := FLinkColorHot;
  2147.   end;
  2148. end;
  2149.  
  2150. { ---------------------------------------------------------------------------- }
  2151.  
  2152. initialization
  2153.   RegisterHtmlTags;
  2154.   RegisterTag(TAG_DYNAMIC, TAG_DYNAMIC_ID);
  2155.   RegisterHtmlAttribs;
  2156.   RegisterHtmlDecodingEntities;
  2157.   RegisterHtmlColors;
  2158.  
  2159. finalization
  2160.   NodeStackItemHandler.Free;
  2161.   RectItemHandler.Free;
  2162.   WordInfoItemHandler.Free;
  2163.  
  2164.   NodeObserverItemHandler.Free;
  2165.  
  2166. end.
  2167.  
  2168.