Skip to content

Commit 473739d

Browse files
committed
Add list support to REML
New tags supported: <ol>, <ul> & <li>. * Major support to REML parser in UREMLDataIO: * Added support for <ul>, <ol> and <li> block level items. * Moved some code to active text units: it logically belonged this. * This change required some major changes to the parser: it previously assumed that block level tags could not be nested. * Comments added to indicate REML version support for entities. * Add support for lists to active text: * Added ordered & unordered lists and list items. * Update renderers re recent method name changes in active text code. * Also extracted some info from REML code that belongs in active text. * Revised REML writer to indent blocks. * Refactor code to use TActiveTextElemCaps for element information. Determination of capabilities was moved from UREMLDataIO unit into UActiveText.UMain where it logically belongs. * Add CSS support for lists: * In UCSSUtils - Added methods to generate CSS "list-style-position" and "list-style-type" properties. * Update main display and Active Text preview dialogue box. * Set list CSS styling for display frame. * Update USnippetExtraHelper: * Revise code that adds paragraphs around text not contained in block. Revision was to get the code to work with blocks nested more than one level deep. * Changes re renamed TActiveTextElemCaps method.
1 parent 5ec55ba commit 473739d

10 files changed

+565
-198
lines changed

Src/ActiveText.UHTMLRenderer.pas

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ procedure TActiveTextHTML.InitialiseTagInfoMap;
134134
ElemKind: TActiveTextActionElemKind;
135135
const
136136
Tags: array[TActiveTextActionElemKind] of string = (
137-
'a', 'strong', 'em', 'var', 'p', 'span', 'h2', 'code'
137+
'a', 'strong', 'em', 'var', 'p', 'span', 'h2', 'code', 'ul', 'ol', 'li'
138138
);
139139
begin
140140
NullAttrs := function(Elem: IActiveTextActionElem): IHTMLAttributes
@@ -194,7 +194,7 @@ function TActiveTextHTML.Render(ActiveText: IActiveText): string;
194194
RenderTextElem(TextElem)
195195
else if Supports(Elem, IActiveTextActionElem, ActionElem) then
196196
begin
197-
if ActionElem.DisplayStyle = dsBlock then
197+
if TActiveTextElemCaps.DisplayStyleOf(ActionElem.Kind) = dsBlock then
198198
RenderBlockActionElem(ActionElem)
199199
else
200200
RenderInlineActionElem(ActionElem);
@@ -244,7 +244,7 @@ procedure TActiveTextHTML.RenderTextElem(Elem: IActiveTextTextElem);
244244
constructor TActiveTextHTML.TCSSStyles.Create;
245245
const
246246
DefaultClasses: array[TActiveTextActionElemKind] of string = (
247-
'external-link', '', '', '', '', 'warning', '', ''
247+
'external-link', '', '', '', '', 'warning', '', '', '', '', ''
248248
);
249249
var
250250
ElemKind: TActiveTextActionElemKind;

Src/ActiveText.UMain.pas

Lines changed: 212 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,17 @@ TActiveTextAttrNames = record
120120
type
121121
/// <summary>Supported types of active text action elements.</summary>
122122
TActiveTextActionElemKind = (
123-
ekLink, // link element: has a URL (inline)
124-
ekStrong, // text formatted as strong (inline)
125-
ekEm, // text formatted as emphasised (inline)
126-
ekVar, // text formatted as variable (inline)
127-
ekPara, // delimits a paragraph (block level)
128-
ekWarning, // text formatted as a warning (inline)
129-
ekHeading, // delimits a heading (block level)
130-
ekMono // text formatted as mono spaced (inline)
123+
ekLink, // link element: has a URL (inline)
124+
ekStrong, // text formatted as strong (inline)
125+
ekEm, // text formatted as emphasised (inline)
126+
ekVar, // text formatted as variable (inline)
127+
ekPara, // delimits a paragraph (block level)
128+
ekWarning, // text formatted as a warning (inline)
129+
ekHeading, // delimits a heading (block level)
130+
ekMono, // text formatted as mono spaced (inline)
131+
ekUnorderedList, // container for unordered lists (block level)
132+
ekOrderedList, // container for ordered list (block level)
133+
ekListItem // list item (block level)
131134
);
132135

133136
type
@@ -157,12 +160,6 @@ TActiveTextAttrNames = record
157160
function GetAttrs: IActiveTextAttrs;
158161
/// <summary>Object describing element's attributes.</summary>
159162
property Attrs: IActiveTextAttrs read GetAttrs;
160-
/// <summary>Returns value that indicates whether element is an inline or
161-
/// block element.</summary>
162-
function GetDisplayStyle: TActiveTextDisplayStyle;
163-
/// <summary>Indicates whether element displays inline or as a block.
164-
/// </summary>
165-
property DisplayStyle: TActiveTextDisplayStyle read GetDisplayStyle;
166163
end;
167164

168165
type
@@ -273,6 +270,162 @@ TActiveTextFactory = class(TNoConstructObject)
273270
IActiveTextAttrs; overload;
274271
end;
275272

273+
type
274+
/// <summary>Provides information about the capabilities of each supported
275+
/// active text element.</summary>
276+
TActiveTextElemCaps = record
277+
strict private
278+
type
279+
/// <summary>Fields used to define an active text element's capabilities.
280+
/// </summary>
281+
TCaps = record
282+
public
283+
var
284+
/// <summary>Determines how element is to be displayed.</summary>
285+
DisplayStyle: TActiveTextDisplayStyle;
286+
/// <summary>Set of elements that may not occur inside the element.
287+
/// </summary>
288+
Exclusions: TActiveTextActionElemKinds;
289+
/// <summary>Set of elements that are permitted as parents of the
290+
/// element.</summary>
291+
/// <remarks>An empty set is taken to mean any element is permitted.
292+
/// </remarks>
293+
RequiredParents: TActiveTextActionElemKinds;
294+
/// <summary>Specifies whether plain text can be contained within the
295+
/// element.</summary>
296+
PermitsText: Boolean;
297+
end;
298+
const
299+
/// <summary>Set of block level elements.</summary>
300+
BlockElems = [
301+
ekPara, ekHeading, ekUnorderedList, ekOrderedList, ekListItem
302+
];
303+
/// <summary>Set of inline elements.</summary>
304+
InlineElems = [
305+
ekLink, ekStrong, ekEm, ekVar, ekWarning, ekMono
306+
];
307+
/// <summary>Set of all elements.</summary>
308+
AllElems = BlockElems + InlineElems;
309+
/// <summary>Map of all elements to their capabilities.</summary>
310+
Map: array[TActiveTextActionElemKind] of TCaps =
311+
(
312+
(
313+
// ekLink
314+
// may contain any inline elements but no block elements
315+
DisplayStyle: dsInline;
316+
Exclusions: BlockElems;
317+
RequiredParents: [];
318+
PermitsText: True;
319+
),
320+
(
321+
// ekStrong
322+
// may contain any inline elements but no block elements
323+
DisplayStyle: dsInline;
324+
Exclusions: BlockElems;
325+
RequiredParents: [];
326+
PermitsText: True;
327+
),
328+
(
329+
// ekEm
330+
// may contain any inline elements but no block elements
331+
DisplayStyle: dsInline;
332+
Exclusions: BlockElems;
333+
RequiredParents: [];
334+
PermitsText: True;
335+
),
336+
(
337+
// ekVar
338+
// may contain any inline elements but no block elements
339+
DisplayStyle: dsInline;
340+
Exclusions: BlockElems;
341+
RequiredParents: [];
342+
PermitsText: True;
343+
),
344+
(
345+
// ekPara
346+
// may contain any inline elements but no block elements
347+
DisplayStyle: dsBlock;
348+
Exclusions: BlockElems;
349+
RequiredParents: [];
350+
PermitsText: True;
351+
),
352+
(
353+
// ekWarning
354+
// may contain any inline elements but no block elements
355+
DisplayStyle: dsInline;
356+
Exclusions: BlockElems;
357+
RequiredParents: [];
358+
PermitsText: True;
359+
),
360+
(
361+
// ekHeading
362+
// may contain any inline elements but no block elements
363+
DisplayStyle: dsBlock;
364+
Exclusions: BlockElems;
365+
RequiredParents: [];
366+
PermitsText: True;
367+
),
368+
(
369+
// ekMono
370+
// may contain any inline elements but no block elements
371+
DisplayStyle: dsInline;
372+
Exclusions: BlockElems;
373+
RequiredParents: [];
374+
PermitsText: True;
375+
),
376+
(
377+
// ekUnorderedList
378+
// may contain only list item elements
379+
DisplayStyle: dsBlock;
380+
Exclusions: AllElems - [ekListItem];
381+
RequiredParents: [];
382+
PermitsText: False
383+
),
384+
(
385+
// ekOrderedList
386+
// may contain only list item elements
387+
DisplayStyle: dsBlock;
388+
Exclusions: AllElems - [ekListItem];
389+
RequiredParents: [];
390+
PermitsText: False;
391+
),
392+
(
393+
// ekListItem
394+
// may contain any inline or block elements except another list
395+
// item
396+
DisplayStyle: dsBlock;
397+
Exclusions: [ekListItem];
398+
RequiredParents: [ekOrderedList, ekUnorderedList];
399+
PermitsText: True;
400+
)
401+
);
402+
public
403+
/// <summary>Returns the display type of the given element.</summary>
404+
class function DisplayStyleOf(const Elem: TActiveTextActionElemKind):
405+
TActiveTextDisplayStyle; static;
406+
/// <summary>Checks whether the given element can contain text.</summary>
407+
class function CanContainText(const Elem: TActiveTextActionElemKind):
408+
Boolean; static;
409+
/// <summary>Checks whether the given Parent element can contain the given
410+
/// Child element.</summary>
411+
class function CanContainElem(
412+
const Parent, Child: TActiveTextActionElemKind): Boolean; static;
413+
/// <summary>Checks whether the given Parent element is required as a
414+
/// parent of the given Child element.</summary>
415+
class function IsRequiredParent(
416+
const Parent, Child: TActiveTextActionElemKind): Boolean; static;
417+
/// <summary>Checks whether the given element is permitted in the root of
418+
/// an active text document, i.e. outside any other block level element.
419+
/// </summary>
420+
class function IsElemPermittedInRoot(const Elem: TActiveTextActionElemKind):
421+
Boolean; static;
422+
/// <summary>Checks whether the given child element is excluded from being
423+
/// a child of the given parent element.</summary>
424+
class function IsExcludedElem(
425+
const Parent, Child: TActiveTextActionElemKind): Boolean; static;
426+
427+
end;
428+
276429

277430
implementation
278431

@@ -354,7 +507,7 @@ TActiveTextTextElem = class(TInterfacedObject,
354507
fText: string;
355508
public
356509
/// <summary>Object constructor. Records given element text and sets
357-
/// required kind for a text element.</summary>
510+
/// required Elem for a text element.</summary>
358511
constructor Create(const Text: string);
359512
/// <summary>Assigns properties of another object to this object.</summary>
360513
/// <param name="Src">IInterface [in] Object whose properties are to be
@@ -375,15 +528,15 @@ TActiveTextActionElem = class(TInterfacedObject,
375528
IActiveTextElem, IActiveTextActionElem, IAssignable, IClonable
376529
)
377530
strict private
378-
/// <summary>Kind of element encapsulated by this object.</summary>
531+
/// <summary>Elem of element encapsulated by this object.</summary>
379532
fKind: TActiveTextActionElemKind;
380533
/// <summary>State of element: opening or closing.</summary>
381534
fState: TActiveTextElemState;
382535
/// <summary>Attributes associated with element.</summary>
383536
fAttrs: IActiveTextAttrs;
384537
public
385538
/// <summary>Object constructor. Creates an action element.</summary>
386-
/// <param name="AKind">TActiveTextElemKind [in] Required kind of element.
539+
/// <param name="AKind">TActiveTextElemKind [in] Required Elem of element.
387540
/// </param>
388541
/// <param name="AAttrs">IActiveTextAttrs [in] Element's attributes.
389542
/// </param>
@@ -402,7 +555,7 @@ TActiveTextActionElem = class(TInterfacedObject,
402555
/// <summary>Returns a cloned instance of this object.</summary>
403556
/// <remarks>Method of IClonable.</remarks>
404557
function Clone: IInterface;
405-
/// <summary>Returns kind of action represented by this element.</summary>
558+
/// <summary>Returns Elem of action represented by this element.</summary>
406559
/// <remarks>Method of IActiveTextActionElem.</remarks>
407560
function GetKind: TActiveTextActionElemKind;
408561
/// <summary>Returns state of element.</summary>
@@ -411,10 +564,6 @@ TActiveTextActionElem = class(TInterfacedObject,
411564
/// <summary>Returns object describing element's attributes.</summary>
412565
/// <remarks>Method of IActiveTextActionElem.</remarks>
413566
function GetAttrs: IActiveTextAttrs;
414-
/// <summary>Returns value that indicates whether element is an inline or
415-
/// block element.</summary>
416-
/// <remarks>Method of IActiveTextActionElem.</remarks>
417-
function GetDisplayStyle: TActiveTextDisplayStyle;
418567
end;
419568

420569
type
@@ -618,7 +767,7 @@ function TActiveText.ToString: string;
618767
if Supports(Elem, IActiveTextTextElem, TextElem) then
619768
SB.Append(TextElem.Text);
620769
if Supports(Elem, IActiveTextActionElem, ActionElem)
621-
and (ActionElem.DisplayStyle = dsBlock)
770+
and (TActiveTextElemCaps.DisplayStyleOf(ActionElem.Kind) = dsBlock)
622771
and (ActionElem.State = fsClose) then
623772
// new line at end of block to separate text at end of closing block
624773
// from text at start of following block
@@ -689,14 +838,6 @@ function TActiveTextActionElem.GetAttrs: IActiveTextAttrs;
689838
Result := fAttrs;
690839
end;
691840

692-
function TActiveTextActionElem.GetDisplayStyle: TActiveTextDisplayStyle;
693-
begin
694-
if GetKind in [ekPara, ekHeading] then
695-
Result := dsBlock
696-
else
697-
Result := dsInline;
698-
end;
699-
700841
function TActiveTextActionElem.GetKind: TActiveTextActionElemKind;
701842
begin
702843
Result := fKind;
@@ -756,5 +897,45 @@ function TActiveTextAttrs.GetEnumerator: TEnumerator<TPair<string, string>>;
756897
Result := fMap.GetEnumerator;
757898
end;
758899

900+
{ TActiveTextElemCapsMap }
901+
902+
class function TActiveTextElemCaps.CanContainElem(const Parent,
903+
Child: TActiveTextActionElemKind): Boolean;
904+
begin
905+
Result := not (Child in Map[Parent].Exclusions);
906+
end;
907+
908+
class function TActiveTextElemCaps.CanContainText(
909+
const Elem: TActiveTextActionElemKind): Boolean;
910+
begin
911+
Result := Map[Elem].PermitsText;
912+
end;
913+
914+
class function TActiveTextElemCaps.DisplayStyleOf(
915+
const Elem: TActiveTextActionElemKind): TActiveTextDisplayStyle;
916+
begin
917+
Result := Map[Elem].DisplayStyle;
918+
end;
919+
920+
class function TActiveTextElemCaps.IsElemPermittedInRoot(
921+
const Elem: TActiveTextActionElemKind): Boolean;
922+
begin
923+
Result := Map[Elem].RequiredParents = [];
924+
end;
925+
926+
class function TActiveTextElemCaps.IsExcludedElem(const Parent,
927+
Child: TActiveTextActionElemKind): Boolean;
928+
begin
929+
Result := Child in Map[Parent].Exclusions;
930+
end;
931+
932+
class function TActiveTextElemCaps.IsRequiredParent(
933+
const Parent, Child: TActiveTextActionElemKind): Boolean;
934+
begin
935+
if Map[Child].RequiredParents = [] then
936+
Exit(True);
937+
Result := Parent in Map[Child].RequiredParents;
938+
end;
939+
759940
end.
760941

Src/ActiveText.URTFRenderer.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ procedure TActiveTextRTF.Render(ActiveText: IActiveText;
196196
RenderTextElem(TextElem, RTFBuilder)
197197
else if Supports(Elem, IActiveTextActionElem, ActionElem) then
198198
begin
199-
if ActionElem.DisplayStyle = dsBlock then
199+
if TActiveTextElemCaps.DisplayStyleOf(ActionElem.Kind) = dsBlock then
200200
RenderBlockActionElem(ActionElem, RTFBuilder)
201201
else
202202
RenderInlineActionElem(ActionElem, RTFBuilder);

Src/ActiveText.UTextRenderer.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function TActiveTextTextRenderer.Render(ActiveText: IActiveText): string;
9797
RenderTextElem(TextElem)
9898
else if Supports(Elem, IActiveTextActionElem, ActionElem) then
9999
begin
100-
if ActionElem.DisplayStyle = dsBlock then
100+
if TActiveTextElemCaps.DisplayStyleOf(ActionElem.Kind) = dsBlock then
101101
RenderBlockActionElem(ActionElem)
102102
else
103103
RenderInlineActionElem(ActionElem);

0 commit comments

Comments
 (0)