Page 1 of 1

How to use Redo after TRVUndoList.AddInfo?

Posted: Wed Jun 12, 2024 5:34 pm
by Vitalii
Hi, I have read other forum threads regarding "Custom Undo" operations, but I did not find an answer.
Based on this topic, I implemented a custom Undo class, and it works fine:

Code: Select all

  
class procedure TRVUndoModifyMathObject.AddUndo(RVData: TRichViewRVData;
  ItemInfo: TRVMathObjectItemInfo);
var
  Edit: TCustomRichViewEdit;
  UndoList: TRVUndoList;
  UndoItem: TRVUndoModifyMathObject;
begin
  if RVData = nil then Exit;
  Edit := TCustomRichViewEdit(RVData.GetAbsoluteRootData.GetParentControl);
  UndoList := TRVEditRVData(RVData).GetUndoList;
  if Assigned(UndoList) then
    begin
      UndoItem := TRVUndoModifyMathObject.Create;
      UndoItem.Action := rvuMisc;
      UndoItem.FInputExpression := ItemInfo.Expression;
      UndoItem.FOutputExpression := ItemInfo.OutputExpression;
      UndoItem.FOutputFormat := ItemInfo.OutputFormat;
      UndoItem.FTitle := ItemInfo.TextAlias;
      UndoItem.FItemNo := RVData.GetItemNo(ItemInfo);
      UndoList.AddInfo(UndoItem, Edit);
    end;
end;
The main code that applies the changes:

Code: Select all

  ...
  if AEdit.CanChange then
    begin
      AEdit.BeginUndoCustomGroup('Modify Math Object');
      TRVUndoModifyMathObject.AddUndo(AEdit.RVData, ItemInfo);
      ItemInfo.Expression := MemoInput.Lines.Text;
      ItemInfo.OutputExpression := EditOutput.Text;
      ItemInfo.OutputFormat := ComboBoxOutputFormat.Text;
      ItemInfo.TextAlias := EditTitle.Text;
      AEdit.Change;
      AEdit.RefreshAll;
    end;
  ...
Unfortunately, Redo does not respond... What should I change/add to make the Redo stack work properly?

Re: How to use Redo after TRVUndoList.AddInfo?

Posted: Thu Jun 13, 2024 9:43 am
by Sergey Tkachenko
Is this main code inside TRVUndoModifyMathObject.Undo?
Can I see the full TRVUndoModifyMathObject implementation?

PS. If you need to modify integer and string properties of an item, there is an alternative solution. You do not need to implement custom undo object. Instead, you can implement extra item properties. Then you can use methods for setting these properties, and it can be undone/redone.
See https://www.trichview.com/forums/viewtopic.php?p=42899

Re: How to use Redo after TRVUndoList.AddInfo?

Posted: Thu Jun 13, 2024 10:31 am
by Vitalii
Yes, implementation of the TRVUndoModifyMathObject.Undo is here:

Code: Select all

procedure TRVUndoModifyMathObject.Undo(RVData: TRichViewRVData);
var
  ItemInfo: TRVMathObjectItemInfo;
begin
  if FItemNo > -1 then
    begin
      ItemInfo := TRVMathObjectItemInfo(RVData.GetItem(FItemNo));
      if Assigned(ItemInfo) then
        begin
          ItemInfo.Expression := FInputExpression;
          ItemInfo.OutputExpression := FOutputExpression;
          ItemInfo.OutputFormat := FOutputFormat;
          ItemInfo.TextAlias := FTitle;
        end;
    end;
end;
As I said, this Undo method works correctly. Other methods are trivial:

Code: Select all

constructor TRVUndoModifyMathObject.Create;
begin
  inherited;
  FInputExpression := '';
  FOutputExpression := '';
  FOutputFormat := '';
  FTitle := '';
  FItemNo := -1;
end;

function TRVUndoModifyMathObject.RequiresFormat: Boolean;
begin
  Result := False;
end;
The "main code" is called through an external editor window (UI). This code is not part of the class TRVUndoModifyMathObject. My logic here is: before making changes, we create an Undo-object, make changes, and change/refresh editor. But I don't know if this Undo-object still works in Redo operations?

Re: How to use Redo after TRVUndoList.AddInfo?

Posted: Thu Jun 13, 2024 12:14 pm
by Sergey Tkachenko
UndoItem.Undo must
1) save the current object state in a new undo item (when undoing, this new undo item will be inserted in the redo list; otherwise, in the undo list)
2) make changes in the object.

Let's see how a very simple undo item is implemented: TRVUndoChangeVAlignInfo, it changes VAlign property of an item.

Code: Select all

procedure TRVUndoChangeVAlignInfo.Undo(RVData: TRichViewRVData);
begin
  TRVEditRVData(RVData).Do_ChangeVAlign(ItemNo, VAlign);
end;
where

Code: Select all

procedure TRVEditRVData.Do_ChangeVAlign(ItemNo: Integer; VAlign: TRVVAlign);
var
  List: TRVUndoList;
  ui:   TRVUndoChangeVAlignInfo;
  item: TCustomRVItemInfo;
begin
  item := GetItem(ItemNo);
  if (item as TRVRectItemInfo).VAlign = VAlign then
    exit;
  // storing the old state in the new undo item 
  List := GetUndoList;
  if List <> nil then
  begin
    ui := TRVUndoChangeVAlignInfo.Create;
    ui.Action := rvuChangeText;
    ui.ItemNo := ItemNo;
    ui.VAlign := TRVRectItemInfo(item).VAlign;
    List.AddInfo(ui, TCustomRichViewEdit(OwnerRichView));
  end;
  // making changes
  TRVRectItemInfo(item).VAlign := VAlign;
end;

Re: How to use Redo after TRVUndoList.AddInfo?

Posted: Thu Jun 13, 2024 3:17 pm
by Vitalii
Yep, I need to add "AddUndo" to the Undo method. Thank you, Sergey)