Skip to content

Commit 1fdc812

Browse files
committed
Add new TActiveText.FirstBlock method
This method returns the first whole block of the current active text object. In most cases this will be the first paragraph, but in the case of lists, it will be the whole list, including any child lists.
1 parent b8f1cea commit 1fdc812

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

Src/ActiveText.UMain.pas

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ TActiveTextAttrNames = record
175175
/// <summary>Appends elements from another given active text object to the
176176
/// current object.</summary>
177177
procedure Append(const ActiveText: IActiveText);
178+
/// <summary>Returns a new IActiveText instance containing just the first
179+
/// block of the current object.</summary>
180+
/// <remarks>
181+
/// <para>The first block is the content of the block level tag that starts
182+
/// the active text. If this block has child blocks (for e.g. an unordered
183+
/// list) then they are included.</para>
184+
/// <para>If the current object is empty then an empty object is returned.
185+
/// </para>
186+
/// </remarks>
187+
function FirstBlock: IActiveText;
178188
/// <summary>Checks if the active text object contains any elements.
179189
/// </summary>
180190
function IsEmpty: Boolean;
@@ -474,6 +484,17 @@ TActiveText = class(TInterfacedObject,
474484
/// </summary>
475485
/// <remarks>Method of IActiveText.</remarks>
476486
procedure Append(const ActiveText: IActiveText);
487+
/// <summary>Returns a new IActiveText instance containing just the first
488+
/// block of the current object.</summary>
489+
/// <remarks>
490+
/// <para>The first block is the content of the block level tag that starts
491+
/// the active text. If this block has child blocks (for e.g. an unordered
492+
/// list) then they are included.</para>
493+
/// <para>If the current object is empty then an empty object is returned.
494+
/// </para>
495+
/// <para>Method of IActiveText.</para>
496+
/// </remarks>
497+
function FirstBlock: IActiveText;
477498
/// <summary>Checks if the element list is empty.</summary>
478499
/// <remarks>Method of IActiveText.</remarks>
479500
function IsEmpty: Boolean;
@@ -719,6 +740,67 @@ destructor TActiveText.Destroy;
719740
inherited;
720741
end;
721742

743+
function TActiveText.FirstBlock: IActiveText;
744+
745+
function IsBlockWithState(Elem: IActiveTextElem; State: TActiveTextElemState):
746+
Boolean;
747+
var
748+
ActionElem: IActiveTextActionElem;
749+
begin
750+
Result := False;
751+
if not Supports(Elem, IActiveTextActionElem, ActionElem) then
752+
Exit;
753+
if TActiveTextElemCaps.DisplayStyleOf(ActionElem.Kind) <> dsBlock then
754+
Exit;
755+
if ActionElem.State <> State then
756+
Exit;
757+
Result := True;
758+
end;
759+
760+
function IsBlockOpener(Elem: IActiveTextElem): Boolean; inline;
761+
begin
762+
Result := IsBlockWithState(Elem, fsOpen);
763+
end;
764+
765+
function IsBlockCloser(Elem: IActiveTextElem): Boolean; inline;
766+
begin
767+
Result := IsBlockWithState(Elem, fsClose);
768+
end;
769+
770+
var
771+
Depth: Cardinal;
772+
Elem: IActiveTextElem;
773+
Idx: Integer;
774+
begin
775+
Result := TActiveText.Create;
776+
if IsEmpty then
777+
Exit;
778+
779+
Elem := GetElem(0);
780+
781+
if not IsBlockOpener(Elem) then
782+
begin
783+
Result.Append(Self);
784+
Exit;
785+
end;
786+
787+
Depth := 1;
788+
Result.AddElem(Elem);
789+
for Idx := 1 to Pred(GetCount) do
790+
begin
791+
Elem := GetElem(Idx);
792+
Result.AddElem(Elem);
793+
// NOTE: we're not checking for matching openers and closers
794+
if IsBlockOpener(Elem) then
795+
Inc(Depth);
796+
if IsBlockCloser(Elem) then
797+
Dec(Depth);
798+
if Depth = 0 then
799+
Break;
800+
end;
801+
// We're not checking for balancing block closer here either:
802+
end;
803+
722804
function TActiveText.GetCount: Integer;
723805
begin
724806
Result := fElems.Count;

0 commit comments

Comments
 (0)