Thanks Sergey for being willing to help me I understand this is complex:
To produce the error it seems RichView Text must be pasted from one RichView to another but make sure there is a background color in the pasted text.
Here is the call stack:
Code: Select all
:770d0144 KERNELBASE.RaiseException + 0x64
:00af6809 TList.Get + $19
:00af6809 TList.Get + $19
CRVFData.TCustomRVFormattedData.PaintTo($19417F10,(0, 0, 505, 880, (0, 0), (505, 880)),False,False,False,False,True,0,0,0,4,nil,nil,False,False)
RVCtrlData.TRVControlData.PaintBuffered((0, 0, 505, 880, (0, 0), (505, 880)))
RVERVData.TRVEditRVData.PaintBuffered((7335540, 12663373, 7335520, 12663405, (7335540, 12663373), (7335520, 12663405)))
RichView.TCustomRichView.Paint
:00c13a4d TCustomControl.PaintWindow + $5D
:00c0d3f3 TWinControl.PaintHandler + $5B
:00c12058 TWinControl.WMPrintClient + $74
:00c07b29 TControl.WndProc + $2C1
:00c0d1e0 TWinControl.WndProc + $6AC
RichView.TCustomRichView.WndProc(???)
uFmBookView.TFmBookView.ReaderWindowProc((792, 3523282062, 4, 0, 1166, 53761, (), 4, 0, (), 0, 0, ()))
twSpTBXDkPanelsEx.TControlWindowProc.SubClassWndProc((792, 3523282062, 4, 0, 1166, 53761, (), 4, 0, (), 0, 0, ()))
:00c0775f TControl.Perform + $27
:00c0dc93 TWinControl.WMPaint + $F3
:00c139e6 TCustomControl.WMPaint + $16
:00c0d1e0 TWinControl.WndProc + $6AC
RichView.TCustomRichView.WndProc(???)
uFmBookView.TFmBookView.ReaderWindowProc((15, 0, 0, 0, 0, 0, (), 0, 0, (), 0, 0, ()))
twSpTBXDkPanelsEx.TControlWindowProc.SubClassWndProc((15, 0, 0, 0, 0, 0, (), 0, 0, (), 0, 0, ()))
:00c0c6a3 TWinControl.MainWndProc + $2F
:00b11402 StdWndProc + $16
:753d9e93 ; C:\WINDOWS\SysWOW64\USER32.dll
:753c800d ; C:\WINDOWS\SysWOW64\USER32.dll
:753c7ab0 ; C:\WINDOWS\SysWOW64\USER32.dll
:753d4329 ; C:\WINDOWS\SysWOW64\USER32.dll
:772cbdc6 ntdll.KiUserCallbackDispatcher + 0x36
:753c7000 USER32.DispatchMessageW + 0x10
And I have simplified the code some only two functions:
readerPaste which is assigned to OnPaste
Code: Select all
var
MuteReaderPasteDelegate: Boolean = False;
TextStyleBeforePaste, ParaStyleBeforePaste: Integer;
procedure TfmBookView.readerPaste(Sender: TCustomRichViewEdit;
var DoDefault: Boolean);
var
sItemNo, sItemOffs, eItemno, eItemOffs: Integer;
CurItem: Integer;
InsertWS: String;
begin
//So Reader paste doesn't take over edtTopics.
{ if edtTopics.Focused then begin
//edtTopics.PasteFromClipboard;
exit;
end else if edtVol.Focused then begin
//edtVol.PasteFromClipboard;
exit;
end;}
// Early validation
if not Assigned(reader) then begin
DoDefault := True;
Exit;
end;
// Safe bounds checking
if reader.ItemCount = 0 then begin
DoDefault := True;
Exit;
end;
try
CurItem := reader.CurItemNo;
// Validate CurItem is within bounds
if (CurItem < 0) or (CurItem >= reader.ItemCount) then begin
CurItem := 0;
end;
// Safe access to style properties
TextStyleBeforePaste := 0;
ParaStyleBeforePaste := 0;
if (reader.CurTextStyleNo >= 0) and (reader.CurTextStyleNo < reader.Style.TextStyles.Count) then
TextStyleBeforePaste := reader.CurTextStyleNo;
if (reader.CurParaStyleNo >= 0) and (reader.CurParaStyleNo < reader.Style.ParaStyles.Count) then
ParaStyleBeforePaste := reader.CurParaStyleNo;
//If Called from Paste To Editor but only from inside of Bookview this boolean helps with that.
if FVerseLinkPasteToEditor and
(TextStyleBeforePaste < reader.Style.TextStyles.Count) and
reader.Style.TextStyles[TextStyleBeforePaste].Jump then begin
//Check if the item is a jump link should be since that is where this came from.
reader.SetSelectionBounds(CurItem,reader.GetOffsAfterItem(CurItem),CurItem,reader.GetOffsAfterItem(CurItem));
InsertWS := _getGlobalIni.ReadString('general','default.bkv.paste.to.editor.str', ' ');
InsertWS := GlobalUtils.StringReplaceU(InsertWS,'#9', #9);
InsertWS := GlobalUtils.StringReplaceU(InsertWS,'#13', #13);
InsertWS := GlobalUtils.StringReplaceU(InsertWS,'#10', #10);
reader.InsertTextW(InsertWS); //Now insert a space, enter something the user desires.
end;
FVerseLinkPasteToEditor := False; //Unfortunately this should be done immeditately after the popup closes but I can't get it to trigger OnClose
//This problem can cause a user to paste in a link and could cause a problem but will work the second time because this will be set.
reader.GetSelectionBounds(sItemNo, sItemOffs, eItemno, eItemOffs, True);
if (sItemNo = eItemNo) and (sItemOffs = eItemOffs) and (sItemOffs = 1) then
Dec(CurItem);
// Validate CurItem again after potential decrement
if CurItem < 0 then CurItem := 0;
//call the WMAfterPaste in 300ms...
//need to use a timer because there is a Application.ProcessMessages; call in
//the uFrmCopyVerses that would 'eat' the PostMessage below
//FAfterPaste_Handle := Handle;
// Save for timer
FAfterPaste_sItemNo := sItemNo;
FAfterPaste_eItemNo := eItemNo;
FAfterPaste_sItemOffs := sItemOffs;
FAfterPaste_eItemOffs := eItemOffs;
FAfterPaste_CurItem := reader.CurItemNo;
FAfterPaste_OldItemCount := reader.ItemCount;
// Only set timer if window handle is valid
{if IsWindow(Handle) then
SetTimer(Handle, 1, 300, @TimerProc_AfterPaste);}
// Use timer instead of Windows callback
FAfterPasteTimer.Enabled := False; // Stop any existing timer
FAfterPasteTimer.Enabled := True; // Start new timer
// Post message directly instead of using timer
//PostMessage(Handle, WM_AFTERPASTE, FAfterPaste_CurItem, FAfterPaste_ItemCount);
//This handler is called with CTRL+V. In that case, the Paste method of RVE
//is called which does NOT handle the HTML format. To handle HTML, we need
//to calle the RichViewActions.OnPaste action. So, if the clipboard contains
//HTML, with call the rvActionPaste2 action and set DoDefault to false, to not
//alow the RVE to handle the Paste itself.
if IsClipboardFormatAvailable(CFRV_HTML) and
(not IsClipboardFormatAvailable(CFRV_RVF)) and
(not IsClipboardFormatAvailable(CFRV_RTF)) and
(not MuteReaderPasteDelegate)then begin
try
MuteReaderPasteDelegate := True;
MainReaderForm.rvActionPaste2.Execute;
DoDefault := False;
finally
MuteReaderPasteDelegate := False;
end
end
else begin
DoDefault := True;
end;
except
on E: Exception do begin
debugOnScreen(PChar('readerPaste error: ' + E.Message));
DoDefault := True;
end;
end;
end;
DoAfterPaste
Code: Select all
var
FixEswordLinksOnPasteRE: TRegExx = ();
{ This code tries to remove dead hyperlinks on paste... I am not sure at all
about it. Commented out above
Need to do ApplyStyleConversion here!!! }
procedure TFmBookView.DoAfterPaste;
var
i, c, p, minIndex, maxIndex: Integer;
RVTag: TRVTag;
cpd: TCheckpointData;
s: string;
name: TRVUnicodeString;
re: Boolean;
readerName: String;
itemStyle: Integer;
sItemNo, sItemOffs, eItemNo, eItemOffs, CurItem, OldItemCount, NewItemCount: Integer;
replacedCount, pastedCount: Integer;
procedure Swap(var A, B: Integer);
var
T: Integer;
begin
T := A;
A := B;
B := T;
end;
begin
// Validate we're still in the correct context
if not (Self.Focused or Self.ContainsControl(Screen.ActiveControl)) then begin
debugOnScreen('DoAfterPaste: BookView no longer active, ignoring');
Exit;
end;
// Validate that the reader is still valid and assigned
if not Assigned(reader) then begin
debugOnScreen('DoAfterPaste: Reader not assigned, ignoring message');
Exit;
end;
// Check if reader handle is valid
if not reader.HandleAllocated then begin
debugOnScreen('DoAfterPaste: Reader handle not allocated, ignoring message');
Exit;
end;
// Validate that we're not in a destroying state
if (csDestroying in ComponentState) or (csLoading in ComponentState) then begin
debugOnScreen('DoAfterPaste: Component destroying/loading, ignoring message');
Exit;
end;
//RvBookUtil.ConvertToUnicodeAndMisc(reader.RVData, FCurModule, False, 0);
if (reader.BiDiMode = rvbdUnspecified) then
if RVShouldUserComplexRendering(reader) then
reader.BiDiMode := rvbdLeftToRight;
//remove possible protections from everything pasted
//with reader.Style do
if Assigned(FCurModule) then begin
//user module: remove protection
if FCurModule.IsUserModule then begin
for i:=0 to reader.Style.TextStyles.Count-1 do
reader.Style.TextStyles[i].Protection := [];
for i:=0 to reader.Style.ParaStyles.Count-1 do
reader.Style.ParaStyles[i].Options := reader.Style.ParaStyles[i].Options - [rvpaoReadOnly, rvpaoNoWrap,
rvpaoDoNotWantReturns];
end
end;
// After paste
NewItemCount := reader.ItemCount;
// Normalize selection
sItemNo := FAfterPaste_sItemNo;
eItemNo := FAfterPaste_eItemNo;
sItemOffs := FAfterPaste_sItemOffs;
eItemOffs := FAfterPaste_eItemOffs;
CurItem := FAfterPaste_CurItem;
OldItemCount := FAfterPaste_OldItemCount;
if sItemNo > eItemNo then
begin
Swap(sItemNo, eItemNo);
end;
if (sItemNo <> eItemNo) or (sItemOffs <> eItemOffs) then
begin
minIndex := sItemNo;
replacedCount := eItemNo - sItemNo + 1;
pastedCount := NewItemCount - OldItemCount + replacedCount;
maxIndex := minIndex + pastedCount - 1;
end
else
begin
pastedCount := NewItemCount - OldItemCount;
if pastedCount <= 0 then
begin
minIndex := -1;
maxIndex := -1;
end
else
begin
// If selection was "select all", pasted items start at 0
if (sItemNo = 0) and (eItemNo = OldItemCount - 1) then
minIndex := 0
// If caret was at start, pasted items start at 0 (offsets are 1)
else if (sItemNo = 0) and (sItemOffs = 1) and (eItemNo = 0) and (eItemOffs = 1) then
minIndex := 0
// If caret was at the end, pasted items start at OldItemCount
else if (sItemNo = OldItemCount - 1) and (sItemOffs = Length(reader.GetItemText(sItemNo)) + 1) then
minIndex := OldItemCount
// Otherwise, pasted items start at caret position
else
minIndex := sItemNo;
// Clamp minIndex to valid range
if minIndex >= NewItemCount then
minIndex := NewItemCount - pastedCount;
if minIndex < 0 then minIndex := 0;
maxIndex := minIndex + pastedCount - 1;
if maxIndex >= NewItemCount then maxIndex := NewItemCount - 1;
end;
end;
if (minIndex >= 0) and (maxIndex >= minIndex) and (maxIndex < NewItemCount) then
for i:= minIndex to maxIndex do begin //JGK was CurItem+reader.ItemCount-ItemCount+1 do begin
// Double-check bounds before accessing
if (i >= 0) AND (i < NewItemCount) then begin //just to be sure...
//SHIFT not pressed (UP)
if GetKeyState(VK_SHIFT) and $ff00 = 0 then begin
try
s := reader.GetItemTag(i);
p := Pos(':', s);
if (p>0) and(p<6) then continue;
//reader.SetItemTag(i, 'REMOVE_ME_' + IntToStr(i));//<------------This can be used instead because it keeps the tags unique and so they don't get conceated in the Normalize I believe.
reader.SetItemTag(i, ''); //<---------IF this is removed then there is no error.
except
on E: Exception do begin
debugOnScreen(PChar('DoAfterPaste GetItemTag error: ' + E.Message));
Continue;
end;
end;
end;
try
// Validate style index before accessing
if (reader.GetItemStyle(i) >= 0) and (reader.GetItemStyle(i) < reader.Style.TextStyles.Count) then begin
if reader.Style.TextStyles[reader.GetItemStyle(i)].Jump then begin
if (Pos('REMOVE_ME_', reader.GetItemTag(i)) > 0) OR (reader.GetItemTag(i) = '') then
reader.Style.TextStyles[reader.GetItemStyle(i)].Jump := False;
end
end;
except
on E: Exception do begin
debugOnScreen(PChar('DoAfterPaste TextStyles error: ' + E.Message));
Continue;
end;
end;
//if pasting tables, add the rvtoEditing (see http://www.trichview.com/forums/viewtopic.php?p=10747#10747)
//bug when pasting from compare view to book view
try
if reader.GetItemStyle(i) = rvsTable then
with TRVTableItemInfo(reader.GetItem(i)) do
Options := Options + [rvtoEditing];
except
on E: Exception do begin
debugOnScreen(PChar('DoAfterPaste Table error: ' + E.Message));
Continue;
end;
end;
//remove checkpoint when pasting from bible view
try
cpd := reader.GetItemCheckpoint(i);
if ( cpd<>nil ) then begin
reader.GetCheckpointInfo(cpd, RVTag, name, re);
if GlobalUtils.StrSameStartU('_TW_VLIDX', RVTag) then
reader.RemoveCheckpointEd(i);
end;
except
on E: Exception do begin
debugOnScreen(PChar('DoAfterPaste Checkpoint error: ' + E.Message));
Continue;
end;
end;
end;//To make sure we are inside of range
end;
//if SHIFT is down, fix e-Sword style links: for pasting from eSword
if GetKeyState(VK_SHIFT) and $ff00 <> 0 then begin
if (minIndex >= 0) and (maxIndex >= minIndex) and (maxIndex < NewItemCount) then
begin
if not FixEswordLinksOnPasteRE.IsInit then
FixEswordLinksOnPasteRE.Create(gc_AutoRefRegex, [roCompiled]);
if maxIndex >= minIndex then begin
RVIterateAllItems(reader.RVData, [], DetectVRefsOnPasteCB, 'DoAfterPaste minIndex to maxIndex', minIndex,
maxIndex);
end else begin
// Fallback: use full range if calculation fails
minIndex := 0;
maxIndex := NewItemCount - 1;
if maxIndex >= minIndex then begin
RVIterateAllItems(reader.RVData, [], DetectVRefsOnPasteCB, 'DoAfterPaste minIndex to maxIndex', minIndex, maxIndex);
end;
debugOnScreen(PChar('DoAfterPaste: Used fallback maxIndex ' + IntToStr(maxIndex)));
end;
cmdAutoDetectVREFsClick(nil);
end;
end;
reader.format; //<----------------------This doesn't seem to do anything
reader.RVData.NormalizeDocument(0,True); //<-----------This triggers the error (I put it here normally it is in some other function and so happens later at various times depending on what a user does.
end;