Page 1 of 1

Replace a Field with a table created at runtime

Posted: Sat Jan 06, 2007 8:35 pm
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

no answer?

Posted: Mon Jan 08, 2007 7:57 pm
by alogrep
Hi
any help?

Posted: Tue Jan 09, 2007 12:57 pm
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;

Posted: Tue Jan 09, 2007 1:05 pm
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.

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

Posted: Tue Jan 09, 2007 10:38 pm
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?

Posted: Thu Jan 11, 2007 2:12 pm
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.

Posted: Thu Jan 11, 2007 8:18 pm
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?

Posted: Fri Jan 12, 2007 6:42 am
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.

sorry now I am lost.....

Posted: Fri Jan 12, 2007 7:25 pm
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;

no answer?

Posted: Sun Jan 14, 2007 8:22 pm
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.....

Posted: Tue Jan 16, 2007 2:24 pm
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;