Page 1 of 2

[Example] DeleteBlankLines, RemoveParagraphBreaks, etc.

Posted: Tue Sep 06, 2005 8:45 pm
by Sergey Tkachenko
This code deletes all empty lines:

Code: Select all

procedure DeleteBlankLines(RVData: TCustomRVData); 
var i,r,c: Integer; 
    table: TRVTableItemInfo; 
begin 
  for i := RVData.ItemCount-1 downto 0 do
    if RVData.IsFromNewLine(i) and (RVData.GetItemStyle(i)>=0) and
      (RVData.GetItemText(i)='') and (RVData.ItemCount>1) and
      ((i = RVData.ItemCount - 1) or RVData.IsFromNewLine(i+1)) then
      RVData.DeleteItems(i,1)
    else if RVData.GetItem(i) is TRVTableItemInfo then
    begin
      table := TRVTableItemInfo(RVData.GetItem(i));
      for r := 0 to table.RowCount-1 do
        for c := 0 to table.ColCount-1 do
          if table.Cells[r,c]<>nil then
            DeleteBlankLines(table.Cells[r,c].GetRVData);
    end;
end;
This procedure cannot be undone/redone. If called for editor, the editor's undo buffer becomes incorrect, so call ClearUndo.
Call:

Code: Select all

DeleteBlankLines(RichViewEdit1.RVData); 
RichViewEdit1.ClearUndo;
RichViewEdit1.Format;
[+] History of changes
2018-May-28: making sure that the deleted item is a single line; changed IsParaStart to IsFromNewLine

Posted: Tue Sep 06, 2005 8:51 pm
by Sergey Tkachenko
This procedure removes all line breaks that can be removed:

Code: Select all

procedure RemoveParagraphBreaks(RVData: TCustomRVData);
var i,r,c: Integer;
    table: TRVTableItemInfo;
begin
  for i := 0 to RVData.ItemCount-1 do
    if (i>0) and RVData.IsFromNewLine(i) and
       not RVData.GetItem(i).GetBoolValue(rvbpFullWidth) and
       not RVData.GetItem(i-1).GetBoolValue(rvbpFullWidth) and
       (RVData.GetItemStyle(i)<>rvsListMarker) then
      RVData.GetItem(i).SameAsPrev := True
    else if RVData.GetItem(i) is TRVTableItemInfo then
    begin
      table := TRVTableItemInfo(RVData.GetItem(i));
      for r := 0 to table.RowCount-1 do
        for c := 0 to table.ColCount-1 do
          if table.Cells[r,c]<>nil then
            RemoveParagraphBreaks(table.Cells[r,c].GetRVData);
    end;
end;
This function cannot be undone/redone. If it's called for editor, information in the editor's undo buffer becomes incorrect, so you need to call RichViewEdit.ClearUndo.

In some cases, documents after calling this functions do not conform to the rules of valid RichView document (see the help file, "Valid
Documents" topic).
In order to fix it, call NormalizeRichView procedure from RVNormalize.pas. This unit is included in RichViewActions.

Call:

Code: Select all

  RemoveParagraphBreaks(RichViewEdit1.RVData);
  NormalizeRichView(RichViewEdit1.RVData);
  RichViewEdit1.ClearUndo;
  RichViewEdit1.Format;

Posted: Wed Nov 02, 2005 9:19 pm
by Sergey Tkachenko
Modification of DeleteBlankLines - removing blank lines only from the end of the document:

Code: Select all

procedure DeleteTrailingBlankLines(RVData: TCustomRVData); 
var i: Integer; 
begin 
  for i := RVData.ItemCount-1 downto 1 do 
    if RVData.IsParaStart(i) and (RVData.GetItemStyle(i)>=0) and 
      (RVData.GetItemText(i)='') then 
      RVData.DeleteItems(i,1)  
    else
      break;
end;
Call:

Code: Select all

DeleteBlankLines(RichViewEdit1.RVData); 
RichViewEdit1.ClearUndo; 
RichViewEdit1.Format;

Posted: Tue Dec 13, 2005 7:40 pm
by Sergey Tkachenko
This procedure converts all single line breaks to "soft" line breaks (which can be added by Shift+Return keys). Double line breaks remain paragraph breaks (which can be added by Return key), but empty line is removed (actually, all empty lines are removed)

Code: Select all

procedure NormalizeLineBreaks(RVData: TCustomRVData);
var i,r,c: Integer;
    table: TRVTableItemInfo;
begin
  for i := RVData.ItemCount-1 downto 0 do
  begin
    if RVData.IsParaStart(i) then
    begin
      // removing empty line
      if RVData.IsParaStart(i) and (RVData.GetItemStyle(i)>=0) and
         (RVData.ItemCount>0) then
      begin
        RVData.DeleteItems(i,1);
        continue;
      end;
      // converting "Return-linebreak" to "Shift+Return-linebreak", if
      // there is no empty line before. Making sure that this conversion is
      // not applied to tables and breaks, items after them, and the very first
      // item
      if (i>0) and not RVData.GetItem(i).GetBoolValue(rvbpFullWidth) and
         not RVData.GetItem(i-1).GetBoolValue(rvbpFullWidth) and
         (RVData.GetItemStyle(i)<>rvsListMarker) and
         not ((RVData.GetItemStyle(i-1)>=0) and (RVData.GetItemText(i-1)='')) then
        RVData.GetItem(i).BR := True;
        continue;
      end;
      // recursive table traversal
      if RVData.GetItem(i) is TRVTableItemInfo then
      begin
        table := TRVTableItemInfo(RVData.GetItem(i));
        for r := 0 to table.RowCount-1 do
          for c := 0 to table.ColCount-1 do
            if table.Cells[r,c]<>nil then
              NormalizeLineBreaks(table.Cells[r,c].GetRVData);
      end;
    end;
end;
Modified 2007-May-31: added (RVData.GetItemStyle(i)<>rvsListMarker) check

Posted: Thu May 31, 2007 1:10 pm
by Sergey Tkachenko
Similar function: removing all single line breaks and empty lines.

Code: Select all

procedure NormalizeLineBreaks2(RVData: TCustomRVData); 
var i,r,c: Integer; 
    table: TRVTableItemInfo; 
begin 
  for i := RVData.ItemCount-1 downto 0 do begin 
    if RVData.IsParaStart(i) then begin 
      // removing empty line 
      if RVData.IsParaStart(i) and (RVData.GetItemStyle(i)>=0) and 
         (RVData.ItemCount>0) then begin 
        RVData.DeleteItems(i,1); 
        continue; 
      end; 
      // Removing line break if there is no empty line before it
      // Making sure that this conversion is 
      // not applied to tables and breaks, items after them, 
     // the very first item and list marker
      if (i>0) and not RVData.GetItem(i).GetBoolValue(rvbpFullWidth) and 
         not RVData.GetItem(i-1).GetBoolValue(rvbpFullWidth) and 
         (RVData.GetItemStyle(i)<>rvsListMarker) and 
         not ((RVData.GetItemStyle(i-1)>=0) and (RVData.GetItemText(i-1)='')) then 
        RVData.GetItem(i).SameAsPrev := True;
        continue; 
      end; 
      // recursive table traversal 
      if RVData.GetItemStyle(i)=rvsTable then begin 
        table := TRVTableItemInfo(RVData.GetItem(i)); 
        for r := 0 to table.Rows.Count-1 do 
          for c := 0 to table.Rows[r].Count-1 do 
            if table.Cells[r,c]<>nil then 
              NormalizeLineBreaks2(table.Cells[r,c].GetRVData); 
      end; 
    end; 
end;
It's recommended to call NormalizeRichView (RVNormalize.pas, can be found in RichViewActions) after calling this procedure.

Posted: Thu May 13, 2010 9:36 pm
by palmeira
I am not sure if this is a bug, but at least for some documents when I use DeleteBlankLines to delete a line, and the next paragraph has a different format (a border), the border is lost.

Posted: Fri May 14, 2010 8:51 pm
by Sergey Tkachenko
DeleteBlandLines assumes that document may have empty text items only as separate lines (paragraphs).
Do you generate your documents yourself?
Try calling NormalizeRichView procedure (RVNormalize.pas, included in RichViewActions) before DeleteBlandLines.

Posted: Fri Feb 28, 2014 5:54 am
by gary
A small improve, Replace Paragraph Breaks with space.

Code: Select all

procedure RemoveParagraphBreaks(RVData: TCustomRVData); 
var i,r,c: Integer; 
    table: TRVTableItemInfo; 
	s: string;
begin 
  for i := 0 to RVData.ItemCount-1 do 
    if (i>0) and RVData.IsFromNewLine(i) and 
       not RVData.GetItem(i).GetBoolValue(rvbpFullWidth) and 
       not RVData.GetItem(i-1).GetBoolValue(rvbpFullWidth) and 
       (RVData.GetItemStyle(i)<>rvsListMarker) then 
	  begin
	    if RVData.GetItemStyle(i) >= 0 then
		begin
	      s := RVData.GetItemText(i);
		  if (Length(s) > 0) then
	        RVData.SetItemText(i, ' ' + s);
		end;
        RVData.GetItem(i).SameAsPrev := True 
	  end
    else if RVData.GetItemStyle(i)=rvsTable then begin 
      table := TRVTableItemInfo(RVData.GetItem(i)); 
      for r := 0 to table.Rows.Count-1 do 
        for c := 0 to table.Rows[r].Count-1 do 
          if table.Cells[r,c]<>nil then 
            RemoveParagraphBreaks(table.Cells[r,c].GetRVData); 
    end; 
end;

Posted: Fri Feb 28, 2014 7:35 am
by Sergey Tkachenko
This function is ok, except for it does not insert a space, if a paragraph started from a non-text item, such as image.

Posted: Sun Mar 02, 2014 2:21 am
by gary
A procedure to trim Leading And Trailing Spaces in paragraph.
Any suggestion is welcome.

Code: Select all

procedure TrimLeadingAndTrailingSpaces(RVData: TCustomRVData);
var
  i,r,c: Integer;
  s: string;
  table: TRVTableItemInfo;
begin
  for i := 0 to RVData.ItemCount - 1 do
  begin
    if RVData.IsParaStart(i) then
    begin

      if RVData.GetItemStyle(i) >= 0 then
      begin
        s := RVData.GetItemText(i);
        s := TrimLeft(s);
        RVData.SetItemText(i, s);
      end;

      if (i - 1 >= 0) and (RVData.GetItemStyle(i - 1) >= 0) then
      begin
        s := RVData.GetItemText(i - 1);
        s := TrimRight(s);
        RVData.SetItemText(i - 1, s);
      end;
    end;

    if (i = RVData.ItemCount - 1) and (RVData.GetItemStyle(i) >= 0) then
    begin
      s := RVData.GetItemText(i);
      s := TrimRight(s);
      RVData.SetItemText(i, s);
    end;

    if RVData.GetItemStyle(i)=rvsTable then
    begin
      table := TRVTableItemInfo(RVData.GetItem(i));
      for r := 0 to table.Rows.Count-1 do
        for c := 0 to table.Rows[r].Count-1 do
          if table.Cells[r,c]<>nil then
            TrimLeadingAndTrailingSpaces(table.Cells[r,c].GetRVData);
    end;
  end;
end;

Posted: Mon Mar 03, 2014 5:33 am
by gary
A procedure is make these methods support apply to selection and support undo. Any suggestion is welcome.
gRVE: TRichViewEdit
RVEditor: TRichViewEdit

Code: Select all

function GetSelectedCells(ARVE: TRichViewEdit; ARowList, AColList: TList): TRVTableItemInfo;
var
  ItemRichViewEdit: TCustomRichViewEdit;
  item: TCustomRVItemInfo;
  i, r, c: Integer;
  StartRow, StartCol, RowOffs, ColOffs: Integer;
begin 
  ARowList.Clear;
  AColList.Clear;
  if ARVE.GetCurrentItemEx(TRVTableItemInfo, ItemRichViewEdit, item) then
  begin
    Result := TRVTableItemInfo(item);
    if Result.GetSelectionBounds(StartRow, StartCol, RowOffs, ColOffs) then
    begin
        for r := 0 to Result.RowCount-1 do
          for c := 0 to Result.ColCount-1 do
            if (Result.Cells[r,c]<>nil) and Result.IsCellSelected(r,c) then
            begin
              ARowList.Add(Pointer(r));
              AColList.Add(Pointer(c));
            end;
    end;
  end;
  if ARowList.Count = 0 then
    Result := nil;
end;

procedure TfM.RemoveParagraphBreaks_Click(Sender: TObject);
var
  table: TRVTableItemInfo;
  i, r, c: Integer;
  RowList, ColList: TList;

  procedure ApplyChangeToSelection(ARVE: TCustomRichViewEdit)
  var
    Stream: TMemoryStream;
  begin
    Stream := TMemoryStream.Create;
    try
      ARVE.SaveRVFToStream(Stream, True);

      Stream.Position := 0;
      gRVE.LoadRVFFromStream(Stream);
      RemoveParagraphBreaks(gRVE.RVData);
      NormalizeRichView(gRVE.RVData);
      gRVE.ClearUndo;
      gRVE.Format;
      Stream.Clear;
      gRVE.SaveRVFToStream(Stream, False);

      Stream.Position := 0;
      ARVE.InsertRVFFromStreamEd(Stream);
    finally
      Stream.Free;
    end;
  end;

begin
  RowList := TList.Create;
  ColList := TList.Create;
  try
    table := GetSelectedCells(RVEditor, RowList, ColList);
    if table <> nil then //Process Table->Select
	begin
      for i := 0 to RowList.Count - 1 do
      begin
        r := Integer(RowList[i]);
        c := Integer(ColList[i]);
        table.Cells[r,c].Edit;
        RVEditor.TopLevelEditor.SelectAll;
        ApplyChangeToSelection(RVEditor.TopLevelEditor);
      end;
	end
	else
	  ApplyChangeToSelection(RVEditor);
  finally
    RowList.Free;
    ColList.Free;
  end;
end;

How to do Delete blank lines from a selection

Posted: Tue Jul 15, 2014 6:40 pm
by gdemers_logilys
I tried the code from the first post and it does what I need but on all the RichView content.

I need to do it on the selection only.

Posted: Fri Aug 01, 2014 7:00 pm
by Sergey Tkachenko
The last post by gary contains the function that is applied to the selection.
I did not test it myself, but I hope it works and can be useful for you.

Posted: Sun Aug 02, 2015 4:38 pm
by Sergey Tkachenko
Line breaks -> paragraph breaks
Converting all line breaks (added with Shift+Enter) to paragraph breaks (added with Enter).
Undoable. If selection is not empty, the procedure is applied to the selection. Otherwise, it is applied to the whole document (if the caret is in a table cell, it is applied to the whole cell).
This procedure is not applied to selected table cells.

Code: Select all

procedure NormalizeLineBreaksEd(rve: TCustomRichViewEdit);
var ItemNo1, Offs1, ItemNo2, Offs2: Integer;
    Whole, FR: Boolean;
  i: Integer;
begin
  rve := rve.TopLevelEditor;
  if not rve.BeforeChange(False) then
    exit;
  rve.GetSelectionBounds(ItemNo1, Offs1, ItemNo2, Offs2, True);
  Whole := (ItemNo1<0) or ((ItemNo1=ItemNo2) and (Offs1=Offs2));
  if Whole then begin
    ItemNo1 := 0;
    ItemNo2 := rve.ItemCount-1;
  end;
  rve.BeginUndoGroup(rvutPara);
  rve.SetUndoGroupMode(True);
  for i := ItemNo2 downto ItemNo1 do
    if rve.IsFromNewLine(i) then
      TRVEditRVData(rve.RVData).Do_BR(i, False, FR);
  rve.SetUndoGroupMode(False);
  rve.Change;
  rve.Reformat;
end;
Paragraph breaks -> line breaks
The opposite procedure.

Code: Select all

procedure MakeSoftLineBreaksEd(rve: TCustomRichViewEdit);
var ItemNo1, Offs1, ItemNo2, Offs2: Integer;
    Whole, FR: Boolean;
  i: Integer;
begin
  rve := rve.TopLevelEditor;
  if not rve.BeforeChange(False) then
    exit;
  rve.GetSelectionBounds(ItemNo1, Offs1, ItemNo2, Offs2, True);
  Whole := (ItemNo1<0) or ((ItemNo1=ItemNo2) and (Offs1=Offs2));
  if Whole then begin
    ItemNo1 := 0;
    ItemNo2 := rve.ItemCount-1;
  end;
  rve.BeginUndoGroup(rvutPara);
  rve.SetUndoGroupMode(True);
  if ItemNo1<1 then
    ItemNo1 := 1;
  for i := ItemNo2 downto ItemNo1 do
    if rve.IsParaStart(i) and (rve.GetItemStyle(i)<>rvsListMarker) and
      not rve.GetItem(i-1).GetBoolValue(rvbpFullWidth) then
       TRVEditRVData(rve.RVData).Do_BR(i, True, FR);
  rve.SetUndoGroupMode(False);
  rve.Change;
  rve.Reformat;
end;
Unfortunately, TRichViewEdit does not have documented methods for converting line breaks, so these procedures use undocumented methods.

Re:

Posted: Tue Jun 20, 2017 6:12 pm
by filo
Sergey Tkachenko wrote: Wed Nov 02, 2005 9:19 pm Modification of DeleteBlankLines - removing blank lines only from the end of the document:

Code: Select all

procedure DeleteTrailingBlankLines(RVData: TCustomRVData); 
var i: Integer; 
begin 
  for i := RVData.ItemCount-1 downto 1 do 
    if RVData.IsParaStart(i) and (RVData.GetItemStyle(i)>=0) and 
      (RVData.GetItemText(i)='') then 
      RVData.DeleteItems(i,1)  
    else
      break;
end;
Call:

Code: Select all

DeleteBlankLines(RichViewEdit1.RVData); 
RichViewEdit1.ClearUndo; 
RichViewEdit1.Format;
Hi Sergey,
thank you for your code, works perfectly with RichViewEdit, but something is wrong with ScaleRichViewEdit in case when there are more than one blank lines at the end of the document AND caret is positioned at the end of document.
In that case I have got an error message List Index out of bounds (x) where x is number of all lines -1.

How to reproduce:
dfm:
- Empty form with SRichViewEdit component
pas:
- Implement code above
runtime:
- Type some character on the first line and 2 blank lines (or make 3 blank lines, it doesn't matter)
- Call DeleteBlankLines


Thank you for your answer.


PS: I'm using RichEdit 16.15.2, demo version