Copying a selected rectangle from one table to another, cell by cell

General TRichView support forum. Please post your questions here
Post Reply
Vitalii
Posts: 71
Joined: Sat Oct 20, 2018 2:55 pm

Copying a selected rectangle from one table to another, cell by cell

Post by Vitalii »

Hi, I'm trying to make it so that copied table cells are inserted into the destination table not as a single embedded table, but into their respective locations (as intuition would suggest). My code works, but the problem is with Undo/Redo. I understand that using "InsertRVFFromStream" for a cell does not undo its contents when Undo is triggered. What would be the correct approach in this case to be able to undo the insertion of content using CTRL+Z? It’s enough to do it once, not for each cell individually.

Code: Select all


procedure TRVClipboardTableRectangle.CopyFromTable(ATable: TRVTableItemInfo);
var
  Row, Col, RowSpan, ColSpan: Integer;
begin
  if ATable = nil then
    raise Exception.Create('ATable=nil in TRVClipboardTableRectangle.CopyCells().');

  if ATable.GetNormalizedSelectionBounds(True, Row, Col, ColSpan, RowSpan) then
    begin
      FStartRow := Row;
      FStartCol := Col;
      FRowCount := RowSpan;
      FColCount := ColSpan;
      ATable.SaveRectangleToStream(FStream, FStartRow, FStartCol, FRowCount, FColCount, True, True);
    end;
end;

procedure TRVClipboardTableRectangle.PasteToTable(ATable: TRVTableItemInfo; AEdit: TCustomRichViewEdit);
var
  TempTable: TRVTableItemInfo;
  StartRow, StartCol, RowSpan, ColSpan: Integer;
  r, c, new_r, new_c: Integer;
  CellStream: TMemoryStream;
  Color: TRVColor;
  Dummy1, Dummy2: Pointer;
  SourceCell, TargetCell: TRVTableCellData;
  CurR, CurC: Integer;
  MainR, MainC: Integer;

begin
  if ATable = nil then
    raise Exception.Create('ATable=nil in TRVClipboardTableRectangle.PasteCells().');

  if AEdit = nil then
    raise Exception.Create('AEdit=nil in TRVClipboardTableRectangle.PasteCells().');

  if not Active then Exit;

  { identify the starting coordinates where the data will be inserted }
  if not ATable.GetNormalizedSelectionBounds(True, StartRow, StartCol, ColSpan, RowSpan) then
    begin
      if ATable.GetEditedCell(CurR, CurC) <> nil then
        begin
          StartRow := CurR;
          StartCol := CurC;
        end
      else
        Exit; { caret is out of table... }
    end;

  Color := clNone;
  Dummy1 := nil;
  Dummy2 := nil;

  TempTable := TRVTableItemInfo.CreateEx(0, 0, AEdit.RVData);
  CellStream := TMemoryStream.Create;

  try
    { load source table rectangle }
    FStream.Position := 0;
    TempTable.LoadFromStream(FStream);

    if (TempTable.RowCount = 0) or (TempTable.ColCount = 0) then Exit;

    for r := 0 to TempTable.RowCount - 1 do
      for c := 0 to TempTable.ColCount - 1 do
        begin
          SourceCell := TempTable.Cells[r, c];

          if Assigned(SourceCell) then
            begin
              new_r := r + StartRow;
              new_c := c + StartCol;

              { are we within the target table range? }
              if (new_r >= ATable.RowCount) or (new_c >= ATable.ColCount) then
                Continue;

              { merge target cells if source cell has ColSpan or RowSpan > 1 }
              if (SourceCell.ColSpan > 1) or (SourceCell.RowSpan > 1) then
                begin
                  { ensure merged area fits within target table bounds }
                  if (new_r + SourceCell.RowSpan <= ATable.RowCount) and
                     (new_c + SourceCell.ColSpan <= ATable.ColCount) then
                    begin
                      ATable.MergeCells(new_r, new_c, SourceCell.ColSpan, SourceCell.RowSpan, True);
                    end;
                end;

              ATable.Rows.GetMainCell(new_r, new_c, MainR, MainC);

              if (MainR >= 0) and (MainC >= 0) and (new_r = MainR) and (new_c = MainC) then
                begin
                  TargetCell := ATable.Cells[MainR, MainC];

                  if Assigned(TargetCell) then
                    begin
                      { copy RVF data of the source cell }
                      CellStream.Clear;
                      SourceCell.SaveRVFToStream(CellStream, False, clNone, nil, nil);

                      { insert RVF data to the target cell }
                      CellStream.Position := 0;
                      TargetCell.Clear;
                      TargetCell.InsertRVFFromStream(CellStream, 0, Color, Dummy1, Dummy2, True, nil, nil);
                      TargetCell.Normalize;

                      { copy cell properties }
                      ATable.SetCellColor(SourceCell.Color, new_r, new_c);
                      ATable.SetCellOpacity(SourceCell.Opacity, new_r, new_c);
                      ATable.SetCellVAlign(SourceCell.VAlign, new_r, new_c);
                      ATable.SetCellBackgroundStyle(SourceCell.BackgroundStyle, new_r, new_c);
                      ATable.SetCellHint(SourceCell.Hint, new_r, new_c);
                      ATable.SetCellRotation(SourceCell.Rotation, new_r, new_c);
                      ATable.SetCellOptions(SourceCell.Options, new_r, new_c);
                      ATable.SetCellTag(SourceCell.Tag, new_r, new_c);

                      if Assigned(SourceCell.BackgroundImage) then
                        ATable.SetCellBackgroundImage(SourceCell.BackgroundImage, new_r, new_c);
                   end;
                end;
            end;
        end;

  finally
    CellStream.Free;
    TempTable.Free;
  end;

  ATable.Changed;
end;
Vitalii
Posts: 71
Joined: Sat Oct 20, 2018 2:55 pm

Re: Copying a selected rectangle from one table to another, cell by cell

Post by Vitalii »

hello community, any updates?
Sergey Tkachenko
Site Admin
Posts: 18210
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Re: Copying a selected rectangle from one table to another, cell by cell

Post by Sergey Tkachenko »

1. To make RVF insertion in ACell of ATable undoable:

Code: Select all

ACell.Edit;
ATable.FInplaceEditor.SelectAll;
ATable.FInplaceEditor.InsertRVFFromStreamEd(...)
2. To make all these changes undoable in one step, call Edit.BeginUndoGroup2(rvutInsert) before, and Edit.EndUndoGroup2(rvutInsert) after, where Edit is the root editor component (containing the main document).
BeginUndoGroup2 and EndUndoGroup2 were designed to group multicell operations.
Post Reply