
{*******************************************************}
{                                                       }
{       RichView                                        }
{       NormalizeRichView - procedure fixing the most   }
{       common possible problems in document            }
{                                                       }
{       Copyright (c) 1997-2003, Sergey Tkachenko       }
{       svt@trichview.com                               }
{       http://www.trichview.com                        }
{                                                       }
{*******************************************************}



unit RVNormalize;

{$I RV_Defs.inc}

interface

uses RVScroll, RVStyle,CRVData,RVItem,RVFuncs, RVTypes;

{
  Call:
    NormalizeRichView(rv.RVData);
    rv.Format;

  This procedure does not update undo/redo buffer in editor, so
  it must be called before the first editing operations after clearing
  and creating/loading document.

  This procedure:
  - fixes incorrect "new line" flags
  - fixes paragraph consisting only with list markers without items
  - fixes incorrect ParaNo assignment
  - removes empty text items (from places where they should not be)
  - combines adjacent text items of the same text style and tag
  So, this procedure fixes issues 1-3 and 5-7 listed in the help topic
  "Valid Documents"
}


procedure NormalizeRichView(RVData: TCustomRVData);

implementation


procedure NormalizeRichView(RVData: TCustomRVData);
    {..........................................}
    procedure InsertEmptyText(RVData: TCustomRVData; ItemNo: Integer);
    var newitem: TCustomRVItemInfo;
        s: TRVRawByteString;
    begin
      newitem := RichViewTextItemClass.Create(RVData);
      newitem.SameAsPrev := True;
      newitem.StyleNo := 0;
      newitem.ParaNo  := 0;
      s := '';
      if RVData.GetRVStyle.TextStyles[0].Unicode then
        newitem.ItemOptions := newitem.ItemOptions + [rvioUnicode];
      newitem.Inserting(RVData, s, False);
      RVData.Items.InsertObject(ItemNo, s, newitem);
      newitem.Inserted(RVData, ItemNo);
    end;
    {..........................................}
var i, ParaNo: Integer;
    ForceParaStart, ItemRequired: Boolean;
    item, item2: TCustomRVItemInfo;
    StoreSub: TRVStoreSubRVData;
    SubRVData: TCustomRVData;
    s: TRVRawByteString;
begin
  ForceParaStart := True;
  ItemRequired   := False;
  i := 0;
  ParaNo := 0;
  while i<RVData.ItemCount do begin
    item := RVData.GetItem(i);
    if ForceParaStart or item.GetBoolValue(rvbpFullWidth) or
      (item.StyleNo=rvsListMarker) then begin
      item.BR := False;
      item.SameAsPrev := False;
    end;
    if not item.SameAsPrev and not item.BR then
      ParaNo := item.ParaNo
    else
      item.ParaNo := ParaNo;
    StoreSub := nil;
    SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdFirst));
    if SubRVData<>nil then begin
      repeat
        NormalizeRichView(SubRVData);
        SubRVData := TCustomRVData(item.GetSubRVData(StoreSub, rvdNext));
      until SubRVData=nil;
      StoreSub.Free;
    end;
    if ItemRequired then
      if item.StyleNo<>rvsListMarker then
        item.SameAsPrev := True
      else begin
        InsertEmptyText(RVData,i);
        item := RVData.GetItem(i);
      end;
    ForceParaStart := item.GetBoolValue(rvbpFullWidth);
    ItemRequired := item.StyleNo = rvsListMarker;
    if (item.StyleNo>=0) then begin
      if not item.SameAsPrev and (RVData.Items[i]='') and (i+1<RVData.ItemCount) and
        RVData.GetItem(i+1).SameAsPrev then begin
        // paragraph is started from empty text item. deleting it
        item2 := RVData.GetItem(i+1);
        item2.PageBreakBefore := item.PageBreakBefore;
        item2.SameAsPrev := False;
        item2.BR := item.BR;
        if (item2.Checkpoint=nil) and (item.Checkpoint<>nil) then begin
          item2.Checkpoint := item.Checkpoint;
          item2.Checkpoint.ItemInfo := item2;
          item.Checkpoint := nil;
        end;
        RVData.DeleteItems(i,1);
        dec(i);
        end
      else if item.SameAsPrev and (i>0) and (RVData.Items[i]='') and
        (RVData.GetItemStyle(i-1)<>rvsListMarker) then begin
        // empty text item at the middle of paragraph. deleting it
        if (item.Checkpoint<>nil) and (i+1<RVData.ItemCount) and
          RVData.GetItem(i+1).SameAsPrev and (RVData.GetItem(i+1).Checkpoint=nil) then begin
          item2 := RVData.GetItem(i+1);
          item2.Checkpoint := item.Checkpoint;
          item2.Checkpoint.ItemInfo := item2;
          item.Checkpoint := nil;
        end;
        RVData.DeleteItems(i,1);
        dec(i);
        end
      else if item.SameAsPrev and (i>0) and (RVData.GetItemStyle(i-1)=item.StyleNo) and
        (item.Checkpoint=nil) and
        RV_CompareTags(item.Tag, RVData.GetItemTag(i-1), rvoTagsArePChars in RVData.Options) then begin
        // the same style as in the previous item in the same paragraph; concatenating
        item := RVData.GetItem(i-1);
        s := RVData.Items[i-1]+RVData.Items[i];
        RVData.ItemAction(rviaTextModifying, item, s, RVData);
        RVData.Items[i-1] := s;
        RVData.DeleteItems(i,1);
        dec(i);
      end
    end;
    inc(i);
  end;
  if ItemRequired then
    InsertEmptyText(RVData, RVData.ItemCount);
end;

end.
