Replace a Field with a table created at runtime

General TRichView support forum. Please post your questions here
Post Reply
alogrep
Posts: 52
Joined: Fri Oct 27, 2006 5:25 pm

Replace a Field with a table created at runtime

Post by alogrep »

Hi Sergey
and Happy New Year.
Hope you can help me.
I want to have a template with fields in it. Most fields are straight string fields e.g. 'Name' would be replaced by Jane Doe. In the middle of the template I also have a field (item name 'details') that should be replaced by a table. The table is created and loaded at runtime, and could have variable # of rows.
So I want to do something like this :

procedure TMyEditor.FillFields(RVData: TCustomRVData);
begin
for i := 0 to RVData.ItemCount-1 do
if RVData.GetItemStyle(i)=rvsTable then begin
..........
end else if RVData.GetItemStyle(i)>=0 then begin
n := RVData.GetItemTag(i);
FieldName := PChar(n);
if (FieldName<>'') and not isblank(rvdata.items) then begin
if FieldName = 'details' then begin
RVData.SetItemTextA(i,'');
if not RichViewEdit1.InsertItem('', mytable) then
showmessage('error inserting table');
end else
RVData.SetItemTextA(i, GetFieldValueFromDatabase(FieldName));
end;
end;
It seems to work EXCEPT that it inserts the table at the beginning of the text in the richviewedit. I want the table inserted exactly where the Field 'details' begins (before I replace it). How can I do that? Also,
InsertItem creates a pointer to mytable or it just takes info from it and creates the cells inside Richviewedit? In other words, do i need to FREE mytable after the InsertItem call?
Thanks
alogrep
Posts: 52
Joined: Fri Oct 27, 2006 5:25 pm

no answer?

Post by alogrep »

Hi
any help?
Sergey Tkachenko
Site Admin
Posts: 17559
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

Code: Select all

procedure TMyEditor.FillFields(RVData: TCustomRVData);
var i: Integer;
     s: String;
begin
  i := RVData.ItemCount-1;
  while i>=0 do begin
    if RVData.GetItemStyle(i)=rvsTable then begin
        ..........
    end else if RVData.GetItemStyle(i)>=0 then  begin
      n := RVData.GetItemTag(i);
      FieldName := PChar(n);
      if (FieldName<>'') and not isblank(rvdata.items[i])  then begin
        if FieldName = 'details' then begin
          mytable := CREATETABLE(RVData);
          mytable.ParaNo := RVData.GetItemPara(i);
          RVData.DeleteItems(i, 1);
          s := '';
          mytable.Inserting(RVData, s, False);
          RVData.Items.InsertObject(i, s, mytable);
          mytable.Inserted(RVData, i);
          if (i>0) and (RVData.GetItemStyle(i-1)=rvsListMarker) then begin
            dec(i);
            RVData.DeleteItems(i, 1);
          end;
        end else
          RVData.SetItemTextA(i, GetFieldValueFromDatabase(FieldName));
      end;
    end;
    dec(i);
  end;
end;
Sergey Tkachenko
Site Admin
Posts: 17559
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

InsertItem cannot be used here. It inserts item in the position of caret, and it requires formatted document (in unformatted documents, position of caret is undefined). Documents become unformatted after using viewer-style methods (like SetItemTextA) until you call Format.

There are no documented viewer-style methods for item insertion (the only exception is InsertRVFFromStream), so my code uses undocumented methods.

Tables cannot be in markered paragraphs, so I added code that removes list marker if it precedes the field. Since this code requires additional change of the item counter (i), I changed "for" to "while".

You need to insert an unique table object and must not free it. So I added a stub code for creating table.
alogrep
Posts: 52
Joined: Fri Oct 27, 2006 5:25 pm

strange: this 'solution' seems to work...

Post by alogrep »

I had found my 'solution', (before your answer) with this code:
for i := 0 to RVData.ItemCount-1 do
if RVData.GetItemStyle(i)=rvsTable then begin
............................................
end else if RVData.GetItemStyle(i)>=0 then begin
n := RVData.GetItemTag(i);
FieldName := PChar(n);
if (FieldName<>'') and not isblank(rvdata.items) then begin
if FieldName = lg('details') then begin
RVData.SetItemTextA(i,'');
rve := RichViewEdit1.TopLevelEditor;
rve.SetSelectionBounds(i, rve.GetOffsBeforeItem(i), i, rve.GetOffsAfterItem(i));
if not RichViewEdit1.InsertItem('', reservetable) then
showmessage('error inserting table');
end else begin
RVData.SetItemTexta(i, GetFieldValueFromDatabase(FieldName));
end;
end;
end;
It seems to work fine, it pust the table exactly where I want it.
Do you see anything wrong with my code?
Sergey Tkachenko
Site Admin
Posts: 17559
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

InsertItem cannot be used here. It inserts item in the position of caret, and it requires formatted document (in unformatted documents, position of caret is undefined). Documents become unformatted after using viewer-style methods (like SetItemTextA) until you call Format.
More info: see the help topic "Viewer vs Editor"
It's not guaranteed that your code will work properly.
Of course, you can call Format after each call of SetItemTextA, but this would be slow. Even using IsertItem is slower than my code, because it reformats part of the document.
alogrep
Posts: 52
Joined: Fri Oct 27, 2006 5:25 pm

Post by alogrep »

ok but i have a prolem.
1. My table must be created in another Form, outside the FillFields procedure. Then it is passed to the "public" session of the Form containing the procedure.
2. My table is
myTable: TRVTableItemInfo
myTable := TRVTableItemInfo.CreateEx(r,9, RichViewEdit1.RVData);
what is the "table" and "createtable" in your code?
3. if I simply remove the line
mytable := CREATETABLE(RVData);
(since mytable is created outside the procedure already) and leave ther rest of the code intact, would it be ok?
Sergey Tkachenko
Site Admin
Posts: 17559
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

You cannot insert the same table more than once.

Well, you can create a copy of this table:

Code: Select all

var Stream: TMemoryStream;
var table: TRVTableItemInfo;

table := TRVTableItemInfo.CreateEx(1,1, RichViewEdit1.RVData);
Stream := TMemoryStream;
myTable.SaveToStream(Stream);
Stream.Position := 0;
table.LoadFromStream(Stream);
Stream.Free;
Then use table instead of myTable in FillFields.
With this method, you are responsible for freeing myTable.
alogrep
Posts: 52
Joined: Fri Oct 27, 2006 5:25 pm

sorry now I am lost.....

Post by alogrep »

Sergey
sorry but i am lost now.
YOu gave me this code:
if FieldName = 'details' then begin
mytable := CREATETABLE(RVData);
mytable.ParaNo := RVData.GetItemPara(i);
RVData.DeleteItems(i, 1);
s := '';
mytable.Inserting(RVData, s, False);
RVData.Items.InsertObject(i, s, mytable);
mytable.Inserted(RVData, i);
if (i>0) and (RVData.GetItemStyle(i-1)=rvsListMarker) then begin
dec(i);
RVData.DeleteItems(i, 1);
end;
end else

WHERE exactly in the ABOVE code would I insert the last code you told me? Please RERMEMBER my table, let's call it ORIGtable, is already created somewhere else, do not give me code to create it, it exists and is loaded. Would this be the code to create ORIGtable?
table := TRVTableItemInfo.CreateEx(1,1, RichViewEdit1.RVData);
Then i can remove this? right? I have already created ORIGtable, long before getting to fillfields.
And this would go were?
Stream := TMemoryStream;
myTable.SaveToStream(Stream);
Stream.Position := 0;
table.LoadFromStream(Stream);
Stream.Free;
alogrep
Posts: 52
Joined: Fri Oct 27, 2006 5:25 pm

no answer?

Post by alogrep »

Hi.
Could you just write the code inside
if FieldName = 'details' then begin
......................
end....
remembering my ORIGTable already exists?
Would this code be correct
if FieldName = 'details' then begin
table := TRVTableItemInfo.CreateEx(y,x, RichViewEdit1.RVData);
Stream := TMemoryStream;
ORIGTable.SaveToStream(Stream);
Stream.Position := 0;
table.LoadFromStream(Stream);
Stream.Free;
ORIGTable.free;
table.ParaNo := RVData.GetItemPara(i);
RVData.DeleteItems(i, 1);
s := '';
table.Inserting(RVData, s, False);
RVData.Items.InsertObject(i, s,table);
table.Inserted(RVData, i);
if (i>0) and (RVData.GetItemStyle(i-1)=rvsListMarker) then begin
dec(i);
RVData.DeleteItems(i, 1);
end;
end.....
Sergey Tkachenko
Site Admin
Posts: 17559
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko »

The code below uses ORIGtable that was created somewhere outside.

Code: Select all

procedure TMyEditor.FillFields(RVData: TCustomRVData); 
var i: Integer; 
     s: String; 
     mytable: TRVTableItemInfo;
     Stream: TMemoryStream;
begin 
  i := RVData.ItemCount-1; 
  while i>=0 do begin 
    if RVData.GetItemStyle(i)=rvsTable then begin 
        .......... 
    end else if RVData.GetItemStyle(i)>=0 then  begin 
      n := RVData.GetItemTag(i); 
      FieldName := PChar(n); 
      if (FieldName<>'') and not isblank(rvdata.items[i])  then begin 
        if FieldName = 'details' then begin 
          mytable := TRVTableItemInfo.CreateEx(1,1, RichViewEdit1.RVData); 
          Stream := TMemoryStream; 
          ORIGTable.SaveToStream(Stream); 
          Stream.Position := 0; 
          mytable.LoadFromStream(Stream); 
          Stream.Free;
          mytable.ParaNo := RVData.GetItemPara(i); 
          RVData.DeleteItems(i, 1); 
          s := ''; 
          mytable.Inserting(RVData, s, False); 
          RVData.Items.InsertObject(i, s, mytable); 
          mytable.Inserted(RVData, i); 
          if (i>0) and (RVData.GetItemStyle(i-1)=rvsListMarker) then begin 
            dec(i); 
            RVData.DeleteItems(i, 1); 
          end; 
        end else 
          RVData.SetItemTextA(i, GetFieldValueFromDatabase(FieldName)); 
      end; 
    end; 
    dec(i); 
  end; 
end;
Post Reply