Access violation on undo in table

General TRichView support forum. Please post your questions here
Post Reply
Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Access violation on undo in table

Post by Marsianin »

There is a problem with Access Violation when making undo inside table in RVE 12.0.4
Just create a table, enter something and press Alt+Backspace several times.
Sometimes access violation fires on pressing Backspace only or changing cell background color.

The problem appears here:

Code: Select all

function TRVEditRVData.GetCurItemNo: Integer;
  {........................................}
  function IndexOf(obj: TCustomRVItemInfo): Integer;
  var ItemNo: Integer;
  begin
    Result := -1;
    if (CaretDrawItemNo>=0) and (CaretDrawItemNo<DrawItems.Count) then begin
      ItemNo := DrawItems[CaretDrawItemNo].ItemNo;
      ...
Sergey Tkachenko
Site Admin
Posts: 17566
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

I cannot reproduce this error in the ActionTest demo.
Please give me exact steps to do to reproduce it.
Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin »

Will try to find the problem but I didn't change anything in my code before migrating to v.12.
Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin »

It happens when undo action moves cursor from one cell to another.
I have this OnCaretMove implementation:

Code: Select all

procedure TMainForm.RichViewEdit1CaretMove(Sender: TObject);
var line,linescount,col:Integer;
begin
  UpdateEditorUI;
  RichViewEdit1.GetCurrentLineCol(line,col);
  with RichViewEdit1 do
    try
      linescount:=GetLineNo(ItemCount-1,GetOffsAfterItem(ItemCount-1));
    except
    end;
  SpTBXLabelItem1.Caption:=WideFormat(TXT_Line[lang],[line,linescount]);
  SpTBXLabelItem2.Caption:=TXT_Column[lang]+' '+IntToStr(col);
end;
When undo moves caret from one cell to another this event fires more that 10 times and after that I'm gettin access violation. But without this event handler (just put Exit; at the beginning) it works fine.
Before 12.0 this code worked fine.
UpdateEditorUI just updates toolbars with the current item information.

Also if there is no undo and you press Alt+Backspace (Ctrl+Z) in editor - Windows sound beeps but in table it does not.
Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin »

Commented UpdateEditorUI and got just one AccessViolation here (not infinite access violation loop as in the first case):

Code: Select all

procedure TCustomRVFormattedData.Item2DrawItem(ItemNo, ItemOffs: Integer;
                             var DrawItemNo, DrawItemOffs: Integer);
var item: TCustomRVItemInfo;
    i: Integer;
begin
  DrawItemNo := -1;
  if ItemNo = -1 then
    exit;
  item := GetItem(ItemNo);
  DrawItemNo := item.DrawItemNo;
  if item.StyleNo<0 then begin
    DrawItemOffs := ItemOffs-DrawItems[DrawItemNo].Offs;
Here is my UpdateEditorUI:

Code: Select all

procedure TMainForm.UpdateEditorUI;
var boo,selexist:Boolean;
    item        :TCustomRVItemInfo;
begin
  if not RichViewEdit1.Visible then Exit;
  boo:=SendMessage(RichViewEdit1.Handle,EM_CANUNDO,0,0)<>0;
  if ReadOnly then boo:=False;
  TBXItem24.Enabled:=boo;
  TBXItem67.Enabled:=boo;
  boo:=SendMessage(RichViewEdit1.Handle,WM_USER+85,0,0)<>0;
  TBXItem25.Enabled:=boo;
  TBXItem66.Enabled:=boo;
  selexist:=RichViewEdit1.RVData.SelectionExists(False,True);
  boo:=selexist;
  TBXItem21.Enabled:=boo;
  TBXItem64.Enabled:=boo;
  TBXItem141.Enabled:=boo;
  If ReadOnly then boo:=False;
  TBXItem142.Enabled:=boo;
  TBXItem20.Enabled:=boo;
  TBXItem65.Enabled:=boo;
  boo:=(Clipboard.FormatCount>0)and(not ReadOnly);
  SpTBXSubMenuItem1.Enabled:=boo;
  SpTBXSubMenuItem2.Enabled:=boo;
  TBXItem22.Enabled:=boo;
  TBXItem63.Enabled:=boo;
  TBXItem140.Enabled:=boo;
  TBXItem189.Enabled:=RichViewEdit1.PageBreaksBeforeItems[RichViewEdit1.CurItemNo];
  item:=RichViewEdit1.GetCurrentItem;
  boo:=(item is TRVGraphicItemInfo)or(item is TRVBreakItemInfo)or(item is TRVTableItemInfo);
  if ReadOnly then boo:=False;
  TBXItem163.Enabled:=boo;
  TBXItem164.Enabled:=boo;
  SpTBXItem64.Visible:=(item is TRVGraphicItemInfo);
  SpTBXSeparatorItem75.Visible:=SpTBXItem64.Visible;
end;
Sergey Tkachenko
Site Admin
Posts: 17566
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

Document may be unformatted in OnCaretMove.
In this event, you can use functions returning caret position (like GetCurrentLineCol), but you cannot use any other methods requiring formatted document.
This includes all functions related to selection.
I recommend to move UpdateEditorUI from this event (leave only procedures depending on the caret position but not on selection).

This is not a new limitation in version 12. It was so from the beginning. If something worked before, it was accidentally.
Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin »

It worked before like a charm. Never got any errors.
So where I should move it? Where is better?
Sergey Tkachenko
Site Admin
Posts: 17566
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

For operations depending on selection - in OnSelect.
Marsianin
Posts: 193
Joined: Sun Sep 25, 2005 11:03 pm

Post by Marsianin »

Ok, thanks. It's working now.
Had to put some other checks to avoid selection bounds errors but now it works.
cychia
Posts: 104
Joined: Mon Jan 16, 2006 1:52 am

I have the similar problem

Post by cychia »

I have a context panel in my form which will display properties of the current item. I found there is not OnItemChanged(ItemStyle) event, so I've handled the OnCaretMoved and check curItemStyle to update my UI context panel accordingly. I got an AV too and from your explanation the AV is caused by GetCurrentItemEx where it tries to access an unformatted doc.

Code: Select all

procedure OnCaretMoved;
var
  nLine, nColumn, nListIndex, nItemStyle: Integer;
  rveTemp: TCustomRichViewEdit;

  function IsCaretInTableCell: Boolean;
  var
    rveTable: TCustomRichViewEdit;
    rveTableItemInfo: TCustomRVItemInfo;
  begin
    Result := RichViewEdit1.GetCurrentItemEx(TRVTableItemInfo, rveTable, rveTableItemInfo);
  end;

begin
  rveTemp := RichViewEdit1.TopLevelEditor;
  with rveTemp do
  begin
     nItemStyle := rveTemp.GetItemStyle(rveTemp.CurItemNo);

    if nItemStyle < 0 then
    begin
      case nItemStyle of
        rvsPicture, rvsHotPicture: Caption := 'Now Picture';
        else
        begin
          if IsCaretInTableCell then
            Caption := 'Now Table'
          else
            Caption := 'Now Normal';
        end;
      end;
    end
    else
    begin
      if IsCaretInTableCell then
        Caption := 'Now Table'
      else
        Caption := 'Now Normal';
    end;
  end;
end;
Then from your suggestion, I have move this part of code to OnSelect, it works fine, but there is a case where this event will not be triggered which is, use your mouse to click a picture in the content, then press -> in your keyboard, then start typing, this event is not triggered so I have no way of knowing current the caret is no longer in a picture so that I can hide my picture context panel.

For this type of context showing, any better event to handle? Thanks.
cychia
Posts: 104
Joined: Mon Jan 16, 2006 1:52 am

Post by cychia »

another doubt is why i get the itemstyle as 0 when the caret is in a table cell?
Sergey Tkachenko
Site Admin
Posts: 17566
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

I tried to place your procedure (OnCaretMoved) in OnCaretMove event.
It works fine for me.
But for any case, I suggest to add

Code: Select all

if RichViewEdit1.ItemCount=0 then
  exit;
at the beginning.
Sergey Tkachenko
Site Admin
Posts: 17566
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

Each table cell initially has one empty text item of the 0th text and the 0th paragraph style.
If you need another style, you can replace it before inserting table.
For example:

Code: Select all

table := TRVTableItemInfo.CreateEx(..., RichViewEdit1.RVData);
for r := 0 to table.RowCount-1 do
  for c := 0 to table.ColCount-1 do begin
    table.Cells[r,c].Clear;
    table.Cells[r,c].Add('', RichViewEdit1.CurTextStyleNo, RichViewEdit1.CurParaStyleNo);
  end;
RichViewEdit1.InsertItem('', table);
cychia
Posts: 104
Joined: Mon Jan 16, 2006 1:52 am

Post by cychia »

Sergey Tkachenko wrote:I tried to place your procedure (OnCaretMoved) in OnCaretMove event.
It works fine for me.
But for any case, I suggest to add

Code: Select all

if RichViewEdit1.ItemCount=0 then
  exit;
at the beginning.
after inserted a table, click one of the cell, start typing, u will get AV
Sergey Tkachenko
Site Admin
Posts: 17566
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

Add this code instead:

Code: Select all

  if (RichViewEdit1.ItemCount=0) or
    (rvstFormattingPart in RichViewEdit1.RVData.State) then
      exit;
By the way, your procedure IsCaretInTableCell has a wrong name.
It returns True if
- the caret is table cell or
- table has multicell selection or
- the caret is to the right or to the left of table.

If you really need to know if the caret is inside table cell, the test is simple:

Code: Select all

 InTableCell := RichViewEdit1.InplaceEditor<>nil
Post Reply