Adding a new line to a canvas.textout - delphi

I am trying to put this to the canvas text out
Name
Flying
Lava
Water
Doing it like so.. it checks if the player should have something under there name like flying,lava,or water. So label text starts as the players name. All labels will have this. and then if any of the "extras" like canwater are true then it will add a new line with the related text. Seen below.
canwater := (FTherePlayers.Player[strtoint(name2)].values['water'] = 'Yes'); //checks if unit can enter water
canlava := (FTherePlayers.Player[strtoint(name2)].values['lava'] = 'Yes'); //checks if unit can enter lava
canfly := (FTherePlayers.Player[strtoint(name2)].values['Flying'] = 'Yes'); //checks if unit can fly
labeltext := FTherePlayers.Player[strtoint(name2)].values['name'];
if canfly then
labeltext := labeltext+ #13#10+ 'Flying';
if canlava then
labeltext := Labeltext+#13#10+'Lava';
if canwater then
labeltext := labeltext+#13#10+'Water';
hexmap1.AddLabelName(Labeltext,posL); //add name to placement label
Now it will give the correct info to the caption. But it never adds the new line, instead it will look something like this
name[][]flying[][]lava[][]water[][]
where [] are small squares
The code i am using for the textout looks like this.
procedure THexmap.AddLabelName(text :string; Position :TPoint);
var
hex_id :string;
P0:tpoint;
begin
with TempMap.canvas do
begin
hex_id := text;
hex_id := text;
{font := self.font;}
p0 := convertcoords(point(Position.X,Position.Y),ptROWCOL);
textout(p0.x - (trunc(textwidth(hex_id) / 2)) ,p0.y- (textheight(hex_id)) ,hex_id);
end;
Refresh;
end;
pretty much it loads the new images or in this case text, to a temp map.. the hex_ID is the name/flying/lava..ect the PO is Where to put it on the map aka row 1 , column 3. As for the textout, i am not sure how that works.. But figure the "newline" code #10#13 is getting messed up there. So any ideas on how i can fix this?
added how i get my XY(tpoint)
{******************************************************************************}
{ This function will return the Row / Col pair based on a given X/Y
for a using application that calls it}
function THexMap.ConvertCoords(point:Tpoint;pointtype:Tpointtype):Tpoint;
var
temp:TPoint;
begin
case PointType of
ptXY: {Convert from x/y to Row/Col}
Begin
temp.x:= round( (point.x + (HexRadius/2) ) / (1.5 * Hexradius));
if odd(temp.x) then
temp.y := round( (point.y + rise) / (rise*2))
else
temp.y := round( point.y / (2*rise));
{ This section insures row / col is good}
if (temp.x < 1) or (temp.y < 1) then
begin
temp.x := 0;
temp.y := 0;
end
else if (temp.y > HexRows) or (temp.x > HexColumns) then
begin
temp.y := 0;
temp.x := 0;
end;
ConvertCoords := temp;
end;
ptRowCol: { Converts Row/Col to X/Y }
begin
if point.x=1 then
temp.x:= HexRadius
else
temp.x := HexRadius+(point.x-1) * (round(1.5 * hexradius));
if odd(point.x) then
if point.y=1 then
temp.y:= rise
else
temp.y := rise+(point.y-1) * (2 * rise)
else
temp.y := (point.y * (2*rise));
ConvertCoords := temp;
end;
end;
end;

TextOut simply treats #13#10 as two characters to draw. That's why you see the squares.
It does not know that you intend to put the text on different lines.
You have to put the text to draw on different lines, e.g. by writing 4 calls to TextOut.
You could also use DrawText from the Win32 API.

var
s:String;
r:TRect;
begin
s := 'Just'#13#10'for'#13#10'demonstration';
r.Left := 10;
r.top := 10;
r.Right := 100;
r.Bottom := 100;
// you can use this with newer Delphiversions
// Canvas.TextRect(r,s, [tfCenter,tfWordBreak]);
// with olderversions you can use this =1 =16
DrawTextEx(Canvas.Handle, PChar(s), Length(s), r, DT_CENTER or DT_WORDBREAK, nil);
end;
as answer of the question how to get rect from point
var
s:String;
r:TRect;
begin
s := 'Just'#13#10'for'#13#10'demonstration';
r.Left := p0.x;
r.top := p0.y;
r.Right := p0.x + 10000; // we will calculate needed rect later
r.Bottom := p0.y + 10000; // via DT_CALCRECT
// you can use this with newer Delphiversions
// Canvas.TextRect(r,s, [tfCenter,tfWordBreak,tfCalcRect]);
// with olderversions you can use this =1 =16 1024
DrawTextEx(Canvas.Handle, PChar(s), Length(s), r, DT_CENTER or DT_WORDBREAK or DT_CALCRECT, nil);
// you can use this with newer Delphiversions
// Canvas.TextRect(r,s, [tfCenter,tfWordBreak]);
// with olderversions you can use this =1 =16
DrawTextEx(Canvas.Handle, PChar(s), Length(s), r, DT_CENTER or DT_WORDBREAK, nil);
end;

Related

FASTREPORT Adding objects dynamically in report script

I try to add TLineView objects to a report.
The number of lines is depending on a certain number, retrieved by the reports dataset.
I have put my code into the scripts initialization part and in a very experimental test version it looks like this:
var nol, i: integer;
child, newChild: TfrxChild;
noteLine1, noteLine2: TfrxLineView;
page: TfrxPage;
begin
page := ReportName;
nol := <DS_MAIN."VOLUME"> /2;
nol := nol + <DS_MAIN."VOLUME"> mod 2;
child3.child := TfrxChild.create(nil);
newchild := child3.child;
newChild.Visible := true;
noteLine1 := TfrxLineView.create(newChild);
noteLine1.name := 'nl1000';
noteLine1.Top := 0.73;
noteLine1.Width := 7.5;
noteLine1.Left := 3;
noteLine1.Visible := true;
noteLine1.Parent.Objects.Remove(noteLine1);
noteLine1.Parent.Objects.Add(noteLine1);
// newChild.Objects.Add(noteLine1);
noteLine2 := TfrxLineView.create(newChild);
noteLine2.name := 'nl1001';
noteLine2.Top := 0.73;
noteLine2.Width := 7.5;
noteLine2.Left := 11.2;
newChild.Objects.Add(noteLine2);
noteLine2.Visible := true;
for i := 1 to nol do begin
Child := TfrxChild.create(nil);
NewChild.child := Child;
newChild := child;
end;
end.
Instead of getting two lines side by side, with a gap between them, I get only a single short line of a length of around 3-4 mm.
The above code is just a snap of my trial-and-error session.
Hope now that there could be anyone to give me some clues.
If I understand your question correctly, you need to consider at least the following:
With your for loop you create bands, not lines. You may try to change the logic and create objects (memos, lines, shapes) with bands as owners.
The objects’ coordinates and sizes are set in pixels, so you need an additional calculation.
From documentation:
Objects’ coordinates and sizes are set in pixels. Since the «Left,»
«Top,» «Width,» and «Height» properties of all objects have the
«Extended» type, you can point out non-integer values. The following
constants are defined for converting pixels into centimeters and
inches:
fr01cm = 3.77953;
fr1cm = 37.7953;
fr01in = 9.6;
fr1in = 96;
The following working example generates five TfrxLineView objects. Just put an empty report on your form and add report title band:
procedure TfrmMain.btnPreviewClick(Sender: TObject);
var
nol, i: integer;
left: Extended;
band: TfrxReportTitle;
line: TfrxLineView;
begin
// Band
band := (report.Report.FindObject('ReportBand') as TfrxReportTitle);
// Lines generation
left := 3;
nol := 5;
for i := 1 to nol do begin
line := TfrxLineView.Create(band);
line.CreateUniqueName;
line.Top := 0.73;
line.Width := fr1cm * 2;
line.Left := left;
left := left + line.Width + 30;
end;
// Report preview
report.ShowReport(False);
end;
This is my final solution:
procedure Child8OnBeforePrint(Sender: TfrxComponent);
var nol, i: integer;
left1, left2: extended;
child, newChild: TfrxChild;
noteLine1, noteLine2, line: TfrxLineView;
page: TfrxPage;
band: TfrxChild;
begin
nol := <DS_MAIN."VOLUME"> /2;
nol := nol + <DS_MAIN."VOLUME"> mod 2;
band := TfrxChild(TRP_ORDER_NOTE.FindObject('Child9'));
// Lines generation
left1 := 3*fr1cm;
left2 := 11.2*fr1cm;
for i := 1 to nol do begin
line := TfrxLineView.Create(band);
line.Name := 'noteLine'+intToStr(1+2*(i-1+trunc(random*1000000))); //Panic solution
line.Top := fr1cm*(0.73 + (i-1)*0.75);
line.Width := 7.5*fr1cm;
line.Left := left1;
if (<DS_MAIN."VOLUME"> mod 2 > 0 ) and (i = nol) then
exit
else
begin
line := TfrxLineView.Create(band);
line.Name := 'noteLine'+intToStr(2*i+trunc(random*1000000));
line.Top := fr1cm*(0.73 + (i-1)*0.75);
line.Width := 7.5*fr1cm;
line.Left := left2;
end;
end;
end;

How to apply an Effect (ie: TGaussianBlurEffect) to a single texture?

I draw several textures on my canvas and on one of them i would like to apply a TGaussianBlurEffect. Problem is that TGaussianBlurEffect will apply to all the container in with it is placed (so all the canvas) where me i want to apply it only to one texture.
i found this function in delphi
procedure TFilterEffect.ProcessTexture(const Visual: TTexture; const Context: TContext3D);
var
Ver: TVertexBuffer;
Ind: TIndexBuffer;
Mat: TTextureMaterial;
begin
if Assigned(FFilter) then
begin
FFilter.ValuesAsTexture['Input'] := Visual;
FFilter.ApplyWithoutCopytoOutput;
if Assigned(Context) then
if Context.BeginScene then
try
Ver := TVertexBuffer.Create([TVertexFormat.Vertex, TVertexFormat.TexCoord0], 4);
Ver.Vertices[0] := Point3D(Context.PixelToPixelPolygonOffset.X,
Context.PixelToPixelPolygonOffset.Y, 0);
Ver.TexCoord0[0] := PointF(0.0, 0.0);
Ver.Vertices[1] := Point3D(Context.PixelToPixelPolygonOffset.X + Visual.Width,
Context.PixelToPixelPolygonOffset.Y, 0);
Ver.TexCoord0[1] := PointF(Visual.Width / TFilterManager.FilterTexture.Width, 0.0);
Ver.Vertices[2] := Point3D(Context.PixelToPixelPolygonOffset.X + Visual.Width,
Context.PixelToPixelPolygonOffset.Y + Visual.Height, 0);
Ver.TexCoord0[2] := PointF(Visual.Width / TFilterManager.FilterTexture.Width,
Visual.Height / TFilterManager.FilterTexture.Height);
Ver.Vertices[3] := Point3D(Context.PixelToPixelPolygonOffset.X,
Context.PixelToPixelPolygonOffset.Y + Visual.Height, 0);
Ver.TexCoord0[3] := PointF(0.0, Visual.Height / TFilterManager.FilterTexture.Height);
Ind := TIndexBuffer.Create(6);
Ind[0] := 0;
Ind[1] := 1;
Ind[2] := 3;
Ind[3] := 3;
Ind[4] := 1;
Ind[5] := 2;
Mat := TTextureMaterial.Create;
Mat.Texture := TFilterManager.FilterTexture;
Context.Clear(0);
Context.SetContextState(TContextState.cs2DScene);
Context.SetContextState(TContextState.csZWriteOff);
Context.SetContextState(TContextState.csZTestOff);
Context.SetMatrix(TMatrix3D.Identity);
Context.DrawTriangles(Ver, Ind, Mat, 1);
Mat.Free;
Ind.Free;
Ver.Free;
finally
Context.EndScene;
end;
end;
end;
but i don't understand it's purpose and how to use it :(

Trouble with dynamic array of timage and tlabel in delphi

I want to add an attachment, and have the form grow longer each time an attachment is added, to make room for a line that holds information about the attachment with a label and some 16X16 images. For this I chose to use a dynamic array (not sure whether that's best). each time an attachment is added, I want to create a new instance of these objects. My code doesn't seem to work. what's wrong with the follwing code?
procedure TVisionMail.AddAttachment(FileString: String);
var
I: Integer;
begin
AttCount := AttCount + 1; // increment attachment count
//set attachment file name
if (AttCount <> 0) and (edAttachment.Text <> '') then
edAttachment.text := edAttachment.text + ';';
edAttachment.text := edAttachment.text + FileString;
//move objects position down to allow space for attachment line
VisionMail.Height := VisionMail.Height + 25;
Panel1.Height := Panel1.Height + 25;
btnSend.Top := btnSend.Top + 25;
btnExit.Top := btnExit.Top + 25;
StatusMemo.Top := StatusMemo.Top + 25;
Memo1.Top := Memo1.Top + 25;
lblBody.Top := lblBody.Top + 25;
//Allocate memory for arrays
SetLength(newImg, AttCount);
SetLength(newlbl, AttCount);
SetLength(newDel, AttCount);
SetLength(newPin, AttCount);
//create new instance and set parents, positions, color, events
newImg[AttCount]:= TImage.Create(VisionMail);
with newImg[AttCount] do
begin
Parent := Panel1;
Top := Memo1.Top - 25;
Left := 408;
Height := 16;
Width := 16;
end;
newlbl[AttCount]:= TLabel.Create(VisionMail);
with newlbl[AttCount] do
begin
Parent := Panel1;
Top := newImg[I].Top + 2;
Left := 397;
Height := 3;
Width := 13;
BiDiMode := bdRightToLeft;
end;
newDel[AttCount] := TAdvToolButton.Create(VisionMail);
with newDel[AttCount] do
begin
Parent := Panel1;
Top := newImg[I].Top;
Left := 440;
Height := 16;
Width := 16;
color := clBtnFace;
colorChecked := clBtnFace;
colorDown := clBtnFace;
colorHot := clBtnFace;
OnClick := btnDelAttClick;
OnMouseEnter := btnDelAttMouseEnter;
OnMouseLeave := btnDelAttMouseLeave;
end;
newPin[AttCount] := TImage.Create(VisionMail);
with newDel[AttCount] do
begin
Parent := Panel1;
Top := newImg[I].Top;
Left := 425;
Height := 16;
Width := 16;
end;
//get Icon for extension of file
lstIcons.GetBitmap(GetIcon(ExtractFileExt
(OpenDialog1.FileName)),
newImg[AttCount].Picture.Bitmap);
newlbl[AttCount].Caption := ExtractFileName(FileString);
end;
The most obvious flaw is that you are writing off the end of all of your arrays. For example, you write
SetLength(newImg, AttCount);
and that means that the valid indices for newImg are 0 to AttCount-1 inclusive. But then you write
newImg[AttCount] := ...
and that is an out of bounds access because the last index is AttCount-1. You do the same for all your array access.
If you compile with range checking enabled, the compiler will generate a runtime error that explains what you have done wrong.
Personally I think you would be better using a record to hold your four components:
TAttachmentControls = record
Img: TImage;
Lbl: TLabel;
.. etc.
end;
And use a TList<TAttachmentControls> as your container.

FastReport - How to displaying data in the form of table?

How can i display data in the form of table in the FastReport ?
Edit
I mean ,I want to create a report like this : (with tabular format).
The easiest way to use FR wizard
from FR File menu > new > Standard report wizard
when you reach the "Layout" page, choose tabular from layout then OK
I think you need to build the grid yourself. Here's a bit of code that builds a grid layout to get you started. You will need to adjust the column widths and add the formatting code (memo.frame) to get your desired look.
procedure CreateHeader(frxDataset: TfrxDBDataSet; Page: TfrxReportPage);
var
i: Integer;
X, Y, ThisWidth: Extended;
HeaderMemo: TfrxCustomMemoView;
Column: TcxGridDBColumn;
begin
Band := TfrxPageHeader.Create(Page);
Band.CreateUniqueName;
Band.SetBounds(0, 0, 0, fr01cm * 7);
Band.Height := edtHeightHeader.Value;
HeaderMemo := CreateMemo(Band);
HeaderMemo.SetBounds(0, 0, PageWidth, 0);
// Set memo style
// Or just add a frame HeaderMemo.Frame....
HeaderMemo.Style := 'Header line';
X := 0;
Y := 0;
Memo := CreateMemo(Band);
Memo.SetBounds(0, Y, X, fr01cm * 6);
Memo.Height := Band.Height - 1;
for i := 0 to pred(frxDataset.Fields.Count) do
begin
ThisWidth := 100;
Memo := CreateMemo(Band);
Memo.SetBounds(X, Y, ThisWidth, fr01cm * 6);
Memo.Text := frxDataset.Fields[i].FieldName;
// Set memo style
// Or just add a frame HeaderMemo.Frame....
Memo.Style := 'Header';
Memo.Height := Band.Height - 1;
X := X + ThisWidth;
end;
HeaderMemo.Height := Band.Height;
end;
procedure CreateFastReportDataBand(frxDataset: TfrxDBDataSet; Page: TfrxReportPage);
var
i: Integer;
X, Y, ThisWidth: Extended;
begin
Band := TfrxMasterData.Create(Page);
Band.CreateUniqueName;
Band.SetBounds(0, CurY, 0, 0);
Band.Height := edtHeightData.Value;
TfrxMasterData(Band).frxDataset := frxDataset;
X := 0;
Y := 0;
for i := 0 to pred(frxDataset.Fields.Count) do
begin
ThisWidth := 100;
Memo := CreateMemo(Band);
Memo.SetBounds(X, Y, ThisWidth, fr01cm * 5);
Memo.Dataset := frxDataset;
Memo.DataField := frxDataset.Fields[i].FieldName;
// Set memo style
// Or just add a frame HeaderMemo.Frame....
Memo.Style := 'Data';
Memo.Height := Band.Height - 1;
X := X + ThisWidth;
end;
end;
It should work ok, but I've not had a chance to test since decoupling it from my application.
It will be possible using Framing Property of Memos.

How to add buttons created at runtime into an array?

I'm sorry if the question looks stupid,but It seems I can't use my head properly in the last hours.
I have a record,
type
TMain = record
Sub:Array of TSubMain; //another record
Button:TsSpeedButton; //this is what we need!
end;
a variable
Main:Array of TMain;
and function:
procedure TFrameSkilLView.CreateButtons(MainBtns,SubMainBtns:byte;title:Array of string);
var i,t,l,w,h:word;
section:string;
begin
l := 41; t:= 57; w := 58; h := 25;
section := 'TOOLBTN_SKILLS_MAIN';
for i := 0 to MainBtns + subMainBtns - 1 do
with TsSpeedButton.Create(nil) do begin
Width := w; Height := h; Top := t; Left := l;
if(i = 0) then SkinData.SkinSection := section + '_C' else skindata.SkinSection := section;
caption := title[i];
Parent := Self;
inc(l,w+4);
if(i = MainBtns - 1) then begin
l := 52; t := 83; w := 64; h := 28;
section := 'TOOLBTN_SKILLS_SUBMAIN';
end;
end;
end;
Lets focus on the loop 'for i := 0 to MainBtns + subMainBtns - 1'.I'd like to add the button created below to the array created above named 'Main:Array of Tmain'.
It should look like this:
for i:=0 to X do
with TsSpeedButton.Create(nil) do begin
Main[i] := this; //where this is the created sSpeedButton.
Howeve,this code can't even be compiled,so I'm asking for a doable way to accomplish what I'm trying to do.
Thank you.
First off, "this" is C++, not Pascal. The Delphi version is "Self". Second, you can't refer to the with-ed object by name. You're better off not using with at all. Try something like this:
for i:=0 to X do
begin
tempButton := TsSpeedButton.Create(nil);
Main[i] := tempButton;
//whatever else
end;

Resources