Page 1 of 1

Access violation on undo in table

Posted: Wed Jan 13, 2010 1:45 am
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;
      ...

Posted: Wed Jan 13, 2010 5:05 pm
by Sergey Tkachenko
I cannot reproduce this error in the ActionTest demo.
Please give me exact steps to do to reproduce it.

Posted: Wed Jan 13, 2010 5:49 pm
by Marsianin
Will try to find the problem but I didn't change anything in my code before migrating to v.12.

Posted: Wed Jan 13, 2010 9:43 pm
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.

Posted: Wed Jan 13, 2010 9:48 pm
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;

Posted: Thu Jan 14, 2010 10:02 am
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.

Posted: Thu Jan 14, 2010 6:11 pm
by Marsianin
It worked before like a charm. Never got any errors.
So where I should move it? Where is better?

Posted: Thu Jan 14, 2010 8:46 pm
by Sergey Tkachenko
For operations depending on selection - in OnSelect.

Posted: Sun Jan 17, 2010 7:27 pm
by Marsianin
Ok, thanks. It's working now.
Had to put some other checks to avoid selection bounds errors but now it works.

I have the similar problem

Posted: Wed Feb 24, 2010 3:33 am
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.

Posted: Wed Feb 24, 2010 4:36 am
by cychia
another doubt is why i get the itemstyle as 0 when the caret is in a table cell?

Posted: Wed Feb 24, 2010 6:44 pm
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.

Posted: Wed Feb 24, 2010 7:11 pm
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);

Posted: Thu Feb 25, 2010 1:19 am
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

Posted: Thu Feb 25, 2010 11:19 am
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