Page 1 of 2
Move Selection to Original Position
Posted: Sat Dec 19, 2009 10:12 pm
by lkessler
I want to do the same thing as "Move Caret to Original Position" in
http://www.trichview.com/forums/viewtopic.php?t=1463 except I want to do it with the selection, instead of the just the caret position.
It needs to work with tables that are only 1 level deep (i.e. they never have tables in the tables).
This is what I tried and I thought might work:
Code: Select all
RVE.BeginUpdate;
RVE.GetSelectionBounds(StartItemNo, StartItemOffs, EndItemNo, EndItemOffs, true);
if RVE.RVData.GetItemStyle(StartItemNo) = rvsTable then begin
TableRVE := RVE.TopLevelEditor;
TableRVE.GetSelectionBounds(TableStartItemNo, TableStartItemOffs, TableEndItemNo, TableEndItemOffs, false);
end;
...
(Add or remove items before the current item)
(Update StartItemNo and EndItemNo to reflect the items added or removed)
...
RVE.SetSelectionBounds(StartItemNo, StartItemOffs, EndItemNo, EndItemOffs);
if RVE.GetCurrentItemEx(TRVTableItemInfo, TableRVE, TableItem) then
TableRVE.SetSelectionBounds(TableStartItemNo, TableStartItemOffs, TableEndItemNo, TableEndItemOffs);
RVE.EndUpdate;
But this does cause an exception when trying to set the selection into a table.
The document is formatted prior to this code.
I have tried adding an RVE.Invalidate just before the RVE.EndUpdate. but that doesn't help.
Any suggestions as to how I can get this to work?
Thanks.
Louis
Posted: Mon Dec 21, 2009 12:48 pm
by Sergey Tkachenko
I am not sure that I understand completely.
But before making a selection inside a table cell, you need to activate its editing.
Use table.EditCell or
RvData := Cell;
RvData := RvData.Edit;
TCustomRVFormattedData(RvData).SetSelectionBounds(...)
Posted: Tue Dec 22, 2009 5:46 am
by lkessler
I don't know what it is, Sergey, but I seem to be having a lot of trouble with this.
Let's see if I can explain it in detail.
What I'm trying to do is save a selection in a table, rebuild the RichViewEdit to include exactly the same table, but possibly as a different item, and then restore the selection.
So for example, as in my code above, the Table is in item StartItemNo. Then to save the selection, I do this:
Code: Select all
var
TableRVE: TCustomRichViewEdit;
if RVE.RVData.GetItemStyle(StartItemNo) = rvsTable then begin
TableRVE := RVE.TopLevelEditor;
TableRVE.GetSelectionBounds(TableStartItemNo, TableStartItemOffs, TableEndItemNo, TableEndItemOffs, false);
end;
But the GetSelectionBounds I used above does not save the cell Row and Column, so I can't pick the cell to edit.
So I guess you're saying that I have to use the TRVTableItemInfo form of GetSelectionBounds and do this:
Code: Select all
var
TableItem: TRVTableItemInfo;
if RVE.RVData.GetItemStyle(StartItemNo) = rvsTable then begin
TableItem := TRVTableItemInfo(RVE.RVData.GetItem(StartItemNo));
TableItem.GetSelectionBounds(TableStartRow, TableStartCol, TableRowOffs, TableColOffs);
end;
Now I recreate my RVE and update StartItemNo to be the item containing the identical table I had before. I want to restore the selection.
But I'm not sure how I do that. If I've got TableStartRow and TableStartCol, then I guess I can select the Cell. Then I can activate its editing. But then how do I get the selection back, since there's only TableRowOffs and TableColOffs? That's only one set of offsets. Not two. What do I put into the SetSelectionBounds?
Posted: Sat Dec 26, 2009 5:55 pm
by Sergey Tkachenko
May be you can use functions from
RVLinear.pas
They are sensitive to adding/deleting contents, but you need not worrying about indices of tables and cells.
Posted: Wed Dec 30, 2009 6:11 am
by lkessler
Okay. Finally further investigation has led me to discover something.
It seems that in my code, the GetSelection Bounds, update items, and then SetSelectionBounds is actually working.
What isn't working is when my cursor starts inside a table, I do a cursor movement down, I do my Get-Update-Set procedure, and then I wait for the cursor movement to happen. Then an exception occurs which you have well documented in your Forums as the "limitation" of being unable to cursor out of a table.
You say that we should use PostMessage to solve this. I've looked through your examples, but I don't see any that tell me how to do it for the arrow and page-up/down keys.
I am thinking that the end of my RVEditKeyDown routine will have to look like this:
Code: Select all
case Key of
VK_UP: PostMessage(Handle, WM_????, 0, 0);
VK_DOWN: PostMessage(Handle, WM_????, 0, 0);
VK_PRIOR: PostMessage(Handle, WM_????, 0, 0);
VK_NEXT: PostMessage(Handle, WM_????, 0, 0);
end;
abort;
What I need to know is what WM_ messages to use for the four keys. I can't find them in the Messages unit. Do you know what I should use here?
Posted: Wed Dec 30, 2009 7:03 pm
by Sergey Tkachenko
I do not understand where is exactly the problem in your code.
Yes, inside OnKeyDown event, you cannot do any operation that destroys the cell inplace editor (for example, moving the caret from this cell to another location).
But do you call your code in OnKeyDown? Why do you need OnKeyDown?
Posted: Wed Dec 30, 2009 7:58 pm
by lkessler
I have pretty well succeeded in virtualizing my very large TRichView documents so as to allow huge document size, but only loading the page you see as well as a couple of pages before and after that.
So on the key down (RVEditKeyDown) event, if a Page Up, Page Down, Arrow Up or Arrow Down is pressed, then I have to ensure that enough "extra" at the top or bottom is available. When it isn't then I may add to the beginning and end of the virtual document and remove any excess from the other end. Then on exit of my RVEditKeyDown event, I can pass control to TRichView's key down processing to now do the movement for the key.
However, since allowing control to pass directly back to TRichView's key down causes an exception, I have to abort that and instead use the PostMessage method to process the key.
Posted: Thu Dec 31, 2009 3:13 am
by lkessler
Oh. Maybe I'm getting it now.
So you are saying that in my OnKeyDown event, for VK_UP, VK_DOWN, VK_PRIOR and VK_NEXT, I should do this:
Code: Select all
PostMessage(Handle, WM_ExtendView, 0, 0);
and then declare
Code: Select all
const
WM_ExtendView = WM_USER + 10;
type
MyForm = class(TForm)
...
procedure WMExtendView(var Msg: TMessage); message WM_ExtendView;
and then call my ExtendView procedure from:
Code: Select all
procedure MyForm.WMExtendView(var Msg: TMessage);
begin
ExtendView;
end;
Then, it should work with the WMExtendView procedure being called after the RIchView Key handling is completed, instead of before.
I'd have to change my logic to allow it to happen after. I'm not sure if that's easy.
Posted: Thu Dec 31, 2009 9:20 am
by Sergey Tkachenko
Yes, this is a correct solution. You can pass a key code in parameters of PostMessage.
Posted: Mon Jan 04, 2010 6:10 am
by lkessler
We've fixed that little sidetrack problem.
But now I need to get back to the original problem: "Move Selection to Original Position"
I am saving my original position as follows:
Code: Select all
RVE.GetSelectionBounds(StartItemNo, StartItemOffs, EndItemNo, EndItemOffs, true);
if RVE.RVData.GetItemStyle(StartItemNo) = rvsTable then begin
TableRVE := RVE.TopLevelEditor;
TableRVE.GetSelectionBounds(TableStartRow, TableStartCol, TableRowOffs, TableColOffs, false);
end;
Then I'm doing something in between, possibly inserting or deleting items before the current selection. While doing that, I update StartItemNo and EndItemNo appropriately and then do a Format.
Now I want to restore the position:
Code: Select all
RVE.SetSelectionBounds(StartItemNo, StartItemOffs, EndItemNo, EndItemOffs);
if RVE.GetCurrentItemEx(TRVTableItemInfo, TableRVE, TableItem) then
TableRVE.SetSelectionBounds(TableStartRow, TableStartCol, TableRowOffs, TableColOffs);
This works perfectly when I'm not in a table. But it doesn't work when I'm in a table.
Do you know what I need to do to get this to set the selection back when the selection is in a table?
Posted: Mon Jan 04, 2010 7:21 pm
by Sergey Tkachenko
In general case, if you cannot store selection as a count of characters from the beginning of the document, you will need to store this information:
[item index of table, row and column]*
StartItemNo, StartItemOffs, EndItemNo, EndItemOffs in the top level editor
*- as many times as many nested tables
Posted: Mon Jan 04, 2010 7:37 pm
by Sergey Tkachenko
Code: Select all
type
TRVSelection = class
private
TableItemNoArr, RowArr, ColArr: array of Integer;
SelStartNo, SelStartOffs, SelEndNo, SelEndOffs: Integer;
public
procedure GetFromRichViewEdit(rve: TCustomRichViewEdit);
procedure SetToRichViewEdit(rve: TCustomRichViewEdit);
end;
{ TRVSelection }
procedure TRVSelection.GetFromRichViewEdit(rve: TCustomRichViewEdit);
var rve2: TCustomRichViewEdit;
Count: Integer;
begin
Count := 0;
rve2 := rve;
while rve2.InplaceEditor<>nil do begin
rve2 := rve2.InplaceEditor as TCustomRichViewEdit;
inc(Count);
end;
SetLength(TableItemNoArr, Count);
SetLength(RowArr, Count);
SetLength(ColArr, Count);
rve2 := rve;
Count := 0;
while rve2.InplaceEditor<>nil do begin
TableItemNoArr[Count] := TRVTableInplaceEdit(rve2.InplaceEditor).FTableItemNo;
RowArr[Count] := TRVTableInplaceEdit(rve2.InplaceEditor).FRow;
ColArr[Count] := TRVTableInplaceEdit(rve2.InplaceEditor).FCol;
inc(Count);
rve2 := rve2.InplaceEditor as TCustomRichViewEdit;
end;
rve2.RVData.GetSelectionBoundsEx(SelStartNo, SelStartOffs, SelEndNo, SelEndOffs, False);
end;
procedure TRVSelection.SetToRichViewEdit(rve: TCustomRichViewEdit);
var i : Integer;
RVData: TCustomRVData;
begin
RVData := rve.RVData;
for i := 0 to High(TableItemNoArr) do
RVData := (RVData.GetItem(TableItemNoArr[i]) as TRVTableItemInfo).
Cells[RowArr[i], ColArr[i]].GetRVData;
RVData := RVData.Edit;
TCustomRVFormattedData(RVData).SetSelectionBounds(SelStartNo, SelStartOffs, SelEndNo, SelEndOffs);
TCustomRVFormattedData(RVData).Invalidate;
end;
Posted: Tue Jan 05, 2010 4:56 am
by lkessler
Sergey. Thank you exceptionally much for drafting out the code. Looking at it as a class, and what you are doing in there is something I would never have thought up.
I implemented it like this:
Code: Select all
var
CurrentSelection: TRVSelection;
...
CurrentSelection.GetFromRichViewEdit(RVE);
...
(code that adds and/or deletes items before the selection)
RVE.Format;
...
CurrentSelection.SetToRichViewEdit(RVE);
I tried it in my program and immediately came up with two problems.
First, I got an access violation on the line:
Since I know I do not have tables nested in my tables, I changed:
Code: Select all
TableItemNoArr, RowArr, ColArr: array of Integer;
to
Code: Select all
TableItemNoArr, RowArr, ColArr: array [0..1] of Integer;
and I commented out the 3 SetLength statements:
Code: Select all
// SetLength(TableItemNoArr, Count);
// SetLength(RowArr, Count);
// SetLength(ColArr, Count);
Then I ran it again and got a: "List index out of bounds (53719630)" exception on the line:
Code: Select all
RVData := (RVData.GetItem(TableItemNoArr[i]) as TRVTableItemInfo).
Cells[RowArr[i], ColArr[i]].GetRVData;
I did check and High(TableItemNoArr) is 1.
I think the problem is that the particular selection I had was not in a table, so the while loop in the GetFromRichViewEdit routine was not executed and the TableItemNoArr was not set.
Could you please help me fix this to handle the "selection not in a table case".
And actually aren't there 3 different possible selections that need to be handled?
1. Outside of tables
2. One or more full cells in a table
3. Within a cell of a table
Thanks.
Louis
Posted: Tue Jan 05, 2010 8:19 am
by Sergey Tkachenko
It's strange, in my tests this code worked ok for selection outside table.
Of course, if you set a fixed number of elements in the array (2 in your case), this will correspond to two levels of nested tables (because the cycle is to High(array)), so it will fail with another nesting level.
Yes, my code ignores the case of multicell selection.
I'll create a fixed version.
Posted: Tue Jan 05, 2010 8:51 am
by Sergey Tkachenko
This is a modified version.
For any case, it does not use dynamic arrays, max nesting level is 12 (I know you need only 1, but I wanted to create an universal procedure).
Now it supports multicell selections.
Code: Select all
const MAXNESTINGLEVEL = 12;
type
TRVSelection = class
private
Count: Integer;
TableItemNoArr, RowArr, ColArr: array [0..MAXNESTINGLEVEL-1] of Integer;
SelStartNo, SelStartOffs, SelEndNo, SelEndOffs: Integer;
MultiCell: Boolean;
StartRow, StartCol, RowOffs, ColOffs: Integer;
public
procedure GetFromRichViewEdit(rve: TCustomRichViewEdit);
procedure SetToRichViewEdit(rve: TCustomRichViewEdit);
end;
{ TRVSelection }
procedure TRVSelection.GetFromRichViewEdit(rve: TCustomRichViewEdit);
var rve2: TCustomRichViewEdit;
begin
Count := 0;
rve2 := rve;
while (rve2.InplaceEditor<>nil) and (Count<MAXNESTINGLEVEL) do begin
rve2 := rve2.InplaceEditor as TCustomRichViewEdit;
inc(Count);
end;
rve2 := rve;
Count := 0;
while (rve2.InplaceEditor<>nil) and (Count<MAXNESTINGLEVEL) do begin
TableItemNoArr[Count] := TRVTableInplaceEdit(rve2.InplaceEditor).FTableItemNo;
RowArr[Count] := TRVTableInplaceEdit(rve2.InplaceEditor).FRow;
ColArr[Count] := TRVTableInplaceEdit(rve2.InplaceEditor).FCol;
inc(Count);
rve2 := rve2.InplaceEditor as TCustomRichViewEdit;
end;
MultiCell := (rve2.RVData.PartialSelectedItem<>nil) and
(rve2.RVData.PartialSelectedItem is TRVTableItemInfo);
if MultiCell then begin
TRVTableItemInfo(rve2.RVData.PartialSelectedItem).GetSelectionBounds(
StartRow, StartCol, RowOffs, ColOffs);
SelStartNo := TRVTableItemInfo(rve2.RVData.PartialSelectedItem).GetMyItemNo;
end
else
rve2.RVData.GetSelectionBoundsEx(SelStartNo, SelStartOffs, SelEndNo, SelEndOffs, False);
end;
procedure TRVSelection.SetToRichViewEdit(rve: TCustomRichViewEdit);
var i : Integer;
RVData: TCustomRVData;
begin
RVData := rve.RVData;
for i := 0 to Count-1 do
RVData := (RVData.GetItem(TableItemNoArr[i]) as TRVTableItemInfo).
Cells[RowArr[i], ColArr[i]].GetRVData;
RVData := RVData.Edit;
if MultiCell then begin
TCustomRVFormattedData(RVData).SetSelectionBounds(SelStartNo,1,SelStartNo,1);
(TCustomRVFormattedData(RVData).GetItem(SelStartNo) as TRVTableItemInfo).
Select(StartRow, StartCol, RowOffs, ColOffs)
end
else
TCustomRVFormattedData(RVData).SetSelectionBounds(SelStartNo, SelStartOffs, SelEndNo, SelEndOffs);
TCustomRVFormattedData(RVData).Invalidate;
end;