Pascal: Project raising External: SIGSEGV when interacting with Form - lazarus

Let's say, there are 3 forms in a project (Form1, Form2, Form3). Form1 has a button on it with the OnClick event set to Form2.Show. This code executes perfectly, however if Form2's code tries to call Form3.Show, then the project raises an EXTERNAL: SIGSEGV pointing to Customform.inc
Project project1 raised exception class 'External: SIGSEGV'
In file '.\include\customform.inc' at line 2196:
Visible := True;
This is exactly what's happening to my project. All forms were properly created and declared, and the units are linked perfectly. The compilation goes fine, without any errors or warnings.
So it is impossible to make the third form visible. But I've discovered that every kind of interaction would result in an External: SIGSEGV error pointing to random pieces of code which compile and run just fine. I just can't figure out the origin of the error.
If I try to execute my project without the debugger, I get an Access Violation error. Failing code:
procedure TWarForm.FormCreate(Sender: TObject);
Begin
Form3.Show;
end;
from
unit work;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
StdCtrls, BGRAFlashProgressBar, AuthUnit;
type
{ TWarForm }
TWarForm = class(TForm)
ArcaneDustIMG: TImage;
ProgressBar: TBGRAFlashProgressBar;
ArcaneEDT: TEdit;
GoldEDT: TEdit;
GoldIMG: TImage;
Label1: TLabel;
Wallpaper: TImage;
procedure FormCreate(Sender: TObject);
procedure WallpaperMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure WallpaperMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure WallpaperMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ private declarations }
public
{ public declarations }
end;
var
WarForm: TWarForm;
MouseIsDown: Boolean;
PX, PY: Integer;
implementation
{$R *.lfm}
{ TWarForm }
procedure TWarForm.WallpaperMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then begin
MouseIsDown := True;
PX := X;
PY := Y;
end;
end;
procedure TWarForm.FormCreate(Sender: TObject);
Begin
Form3.Show;
end;
procedure TWarForm.WallpaperMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if MouseIsDown then begin
SetBounds(WarForm.Left + (X - PX), WarForm.Top + (Y - PY), WarForm.Width, WarForm.Height);
end;
end;
procedure TWarForm.WallpaperMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
MouseIsDown:=False;
end;
end.

You have to create the forms either manualy or set them to "auto create" in your IDE
To Create them manualy just change your code slightly:
TWarForm = class(TForm)
ArcaneDustIMG: TImage;
ProgressBar: TBGRAFlashProgressBar;
ArcaneEDT: TEdit;
GoldEDT: TEdit;
GoldIMG: TImage;
Label1: TLabel;
Wallpaper: TImage;
Form2: TForm2; // insert Form2
Form3: TForm3; // and Form3
procedure FormCreate(Sender: TObject);
procedure WallpaperMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure WallpaperMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure WallpaperMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ private declarations }
public
{ public declarations }
end;
..
procedure TWarForm.FormCreate(Sender: TObject);
Begin
Form3 := TForm3.Create(Self);
Form3.Show;
end;
If you do so, don't forget to call Form3.Free at the end of your application execution.

Related

Free Pascal / Lazarus : Why is the "FormCloseQuery" Event not called in my example?

I wrote a small testprogram to try out the FormCreate and the FormCloseQuery procedure. The FormCreate works fine, but the FormCloseQuery just doesn't want to execute. Did I overlook something? Pressing the "X" on a form-window or using the close method, both doesn't work!
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
(...)
procedure TForm1.FormCreate(Sender: TObject);
beginn
//gets executed without problems
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
case MessageDlg('Question', mtConfirmation, [mbyes, mbno, mbcancel], 0) of
mrYes:
begin
ShowMessage('yes');
CanClose := true;
end;
mrNo:
begin
ShowMessage('no');
CanClose := true;
end;
else
begin
ShowMessage('cancel');
CanClose := true;
end;
end;
end;
Here's the full code:
The lpr-File:
program project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, Unit1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource:=True;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
The Unit-File:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: boolean);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage('FormCreate Procedure wurde gestartet');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
case MessageDlg('Question', mtConfirmation, [mbyes, mbno, mbcancel], 0) of
mrYes:
begin
ShowMessage('yes');
CanClose := true;
end;
mrNo:
begin
ShowMessage('no');
CanClose := true;
end;
else
begin
ShowMessage('cancel');
CanClose := true;
end;
end;
end;
end.
The solution is/was to hook up the form's OnCloseQuery event of the form to this procedure. Here's a short description how to do it:
Bring up and select the form (in my case "Form1: TForm1") in the object inspector (see explanation below).
In the object inspector go to the tab "events"
Locate the "OnCloseQuery"-Event and select "FormCloseQuery" if you already declared and written the procedure as it was the case in my example. (If you haven't declared/implemented it double click in the dropdown box or click on the button next to it, the one with the three dots, an an empty "OnCloseQuery"-procedure procedure will be added automatically. The code editor will jump directly to the new procedure.)
To bring up / select the form in the object inspector, open the object inspector (F11). If you are looking at the code editor bring up the form first (F12) and click on it. This should bring it up in the object inspector. Make sure the form (the top element in the list) is selected and none of it's components (like buttons etc.).
Here's another explanation: http://www.delphigroups.info/2/b2/444056.html

Defining borders for image movement in Delphi [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I need to move an image with along X-axis and, when it reaches defined borders, stop moving (I'm making my own trackbar). I can't find out how to define borders. With my code when it reaches border, it stucks there and unable to move. Here's the code
var
PinCurrentPosition,PinStartingPosition:integer;
move:boolean;
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered := True;
Image5.Picture.LoadFromFile('Untitled2.bmp');
PinStartingPosition:=Image5.Left;
end;
procedure TForm1.Image5MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (button <> mbLeft) then move:=false
else
begin
move:=true;
PinCurrentPosition:=x;
end;
end;
procedure TForm1.Image5MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if move and ((PinStartingPosition-75)<Image5.Left)
and ((PinStartingPosition+75)>Image5.Left) then
Image5.Left:=Image5.Left+x-PinCurrentPosition;
end;
procedure TForm1.Image5MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
move:=false;
end;
You should add an Else to Image5MouseMove procedure to correct image position if it is outside of movable area:
procedure TForm1.Image5MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if
move and
(Image5.Left>(PinStartingPosition-75)) and
(Image5.Left<(PinStartingPosition+75))
then
Image5.Left:=Image5.Left+x-PinCurrentPosition;
else if Image5.Left<=(PinStartingPosition-75) then
Image5.Left:= PinStartingPosition-75+1
else if Image5.Left>=(PinStartingPosition+75) then
Image5.Left:= PinStartingPosition+75-1;
end;

Delphi PNG image displays rectangle BUG around when move / load image

around png image appears strange rectangle on Load image or when image is moved.
Rectangle appears rarely in 24bit PNG, or jpg, but with 32bit PNG is problem. Does anyone know what causes it? PNG are created in Photoshop. I tried also gimp but same problem.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, pngimage, jpeg, ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Image2: TImage;
Timer1: TTimer;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Label1: TLabel;
procedure Timer1Timer(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
png:TPngImage;
rs:TResourceStream;
ms:TMemoryStream;
implementation
{$R *.dfm}
{$R FB.RES}
procedure TForm1.Button1Click(Sender: TObject);
begin
rs:=TResourceStream.Create(hInstance,'24bitpng',RT_RCDATA);
png:=TPngImage.Create;
png.LoadFromStream(rs);
Image1.Picture.Graphic:=png;
rs.Free;
Label1.Caption:=Button1.Caption;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
rs:=TResourceStream.Create(hInstance,'32bitpng',RT_RCDATA);
png:=TPngImage.Create;
png.LoadFromStream(rs);
Image1.Picture.Graphic:=png;
rs.Free;
Label1.Caption:=Button2.Caption;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Form1.Close;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
Timer1.Enabled:=True;
Image2.Left:=0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered:=True; //This did the job, now no flickering around
Form1.BorderStyle:=bsnone;
Form1.Position:=poScreenCenter;
Label1.Caption:=Button1.Caption;
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Screen.Cursor:=crSizeAll;
ReleaseCapture;
SendMessage(Form1.Handle, WM_SYSCOMMAND, 61458, 0) ;
Screen.Cursor:=crDefault;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if image2.Left<300 then
image2.Left:=image2.Left+2
else
Timer1.Enabled:=False;
end;
end.
This i tried.
...
protected
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
...
begin
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result := 1;
end;
end.
Your basic approach is flawed. You are not expected to use TImage controls to show animations. These controls are designed for displaying static images. As a crude solution you could enable double buffering for the form. Do this by setting DoubleBuffered to True. This has side-effects that may be undesirable. In any case, the entire approach should make you feel queasy.
The right approach is to render the entire image to a drawing surface. In an ideal world you would have a single windowed control that rendered the background in response to WM_ERASEBKGND, and then painted the dynamic content in response to WM_PAINT. This is what I would do.
As a simpler half way house you could use a TPaintBox or perhaps even the form's OnPaint handler. These approaches would have you painting the entire image in response to WM_PAINT. That should be free of flicker. If not then perhaps you'll need to resort to painting to an off-screen bitmap and then blitting that to the paint surface.

Delphi 7 - PNG Image isn't showing

I am using Delphi 7 Pro. I have installed the PNG Component in my project, but whenever I load a PNG image into the Image1 component, the application starts but its main form is invisible. If I load a JPEG or a bitmap file, the form is shown. Here's the code I'm using in my form constructor:
procedure TFMain.FormCreate(Sender: TObject);
var
regn, tmpRegn, x, y: integer;
nullClr: TColor;
Settings: TInifile;
begin
FMain.Brush.Bitmap := Image1.Picture.Bitmap;
nullClr := Image1.Picture.Bitmap.Canvas.Pixels[0, 0];
regn := CreateRectRgn(0, 0, Image1.Picture.Graphic.Width, Image1.Picture.Graphic.Height);
for x := 1 to Image1.Picture.Graphic.Width do
for y := 1 to Image1.Picture.Graphic.Height do
if Image1.Picture.Bitmap.Canvas.Pixels[x - 1, y - 1] = nullClr then
begin
tmpRegn := CreateRectRgn(x - 1, y - 1, x, y);
CombineRgn(regn, regn, tmpRegn, RGN_DIFF);
DeleteObject(tmpRegn);
end;
SetWindowRgn(FMain.Handle, regn, True);
end;
Why is this happening ? What should I change so I can use PNG image in my Image1 ?
The whole code:
unit UnitMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gauges, Buttons, IniFiles, StdCtrls, OleCtrls, SHDocVw, ExtCtrls,
Wininet, ImgBtn, ComCtrls, ShlObj, ComObj, ActiveX, jpeg;
type
TFMain = class(TForm)
Gauge1: TGauge;
Gauge2: TGauge;
Image1: TImage;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
ImgBtn1: TImgBtn;
ImgBtn2: TImgBtn;
ImgBtn3: TImgBtn;
ImgBtn4: TImgBtn;
Panel1: TPanel;
WebBrowser1: TWebBrowser;
Image2: TImage;
Image3: TImage;
Image4: TImage;
Label4: TLabel;
ImgBtn5: TImgBtn;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure ImgBtn4Click(Sender: TObject);
procedure ImgBtn3Click(Sender: TObject);
procedure ImgBtn2Click(Sender: TObject);
procedure ImgBtn1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ImgBtn5Click(Sender: TObject);
procedure WebBrowser1NavigateComplete2(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
procedure Timer1Timer(Sender: TObject);
procedure UpdateRevision(Rev: string);
private
{ Private declarations }
public
Draging: Boolean;
X0, Y0: integer;
end;
var
FMain: TFMain;
USettings : TStrings;
implementation
uses
Frm2, GetFilesThr, Misc;
{$R *.dfm}
procedure TFmain.UpdateRevision(Rev: string);
var
Settings: TInifile;
begin
Settings := TInifile.Create(USettings[0] + '_settings.ini');
Settings.WriteString('main', 'AtRevision', Rev);
Settings.Free;
end;
function LoadSettings(): bool;
var
Settings: TInifile;
begin
Result := False;
USettings := TStringlist.Create;
USettings.Add(GetCurrentDir + '\');
if(FileExists(USettings[0] + '_settings.ini')) then
begin
Settings := TInifile.Create(USettings[0] + '_settings.ini');
USettings.Add(Settings.ReadString('main', 'NewsUrl', ''));
USettings.Add(Settings.ReadString('main', 'UpdatesUrl', ''));
USettings.Add(Settings.ReadString('main', 'LinkName', 'Lineage II'));
USettings.Add(Settings.ReadString('main', 'Installed', '0'));
USettings.Add(Settings.ReadString('main', 'CreateBackup', '0'));
USettings.Add(Settings.ReadString('main', 'AtRevision', '0'));
USettings.Add(Settings.ReadString('main', 'RunCustom', 'system\l2.exe'));
Settings.Free;
Result := True;
end
end;
// ρξηδΰες πλϋκ νΰ ρεα νΰ πΰαξχεμ ρςξλε
procedure CreateDesktopIcon(ilname, WorkDir, desc : string);
var
IObj: IUnknown;
SLink: IShellLink;
PFile: IPersistFile;
desk: string;
lnkpath: WideString;
begin
if(ilname <> '') then
begin
SetLength(desk, MAX_PATH + 1);
SHGetSpecialFolderPath(0, PAnsiChar(desk), CSIDL_DESKTOPDIRECTORY, False);
lnkpath:= PChar(desk) + '\' + ilname + '.lnk';
IObj := CreateComObject(CLSID_ShellLink);
SLink := IObj as IShellLink;
PFile := IObj as IPersistFile;
with SLink do
begin
SetDescription(PChar(desc));
SetPath(PChar(Application.ExeName));
SetWorkingDirectory(PAnsiChar(WorkDir));
end;
PFile.Save(PWChar(WideString(lnkpath)), FALSE);
end;
end;
procedure TFMain.FormCreate(Sender: TObject);
var
regn, tmpRegn, x, y: integer;
nullClr: TColor;
s_load: bool;
Settings: TInifile;
begin
s_load := LoadSettings();
if (s_load) then
begin
if (USettings[4] = '0') then
begin
Settings := TInifile.Create(USettings[0] + '_settings.ini');
Settings.WriteString('main','Installed', '1');
Settings.Free;
CreateDesktopIcon(USettings[3], USettings[0], 'Play Lineage II');
end;
end
else
begin
FMain.Timer1.Enabled := False;
ShowMessage('ERROR: _settings.ini Not Found !');
Application.Terminate; // .close ηδερό νε οπξιδες
end;
// Νΰβξδθμ κπΰρθβξρςό νΰ τξπμσ ...
FMain.brush.bitmap := Image1.picture.bitmap;
nullClr := image1.picture.Bitmap.Canvas.Pixels[0, 0];
regn := CreateRectRgn(0, 0, image1.picture.Graphic.Width, image1.picture.Graphic.Height);
for x := 1 to image1.picture.Graphic.Width do
for y := 1 to image1.picture.Graphic.Height do
if image1.picture.Bitmap.Canvas.Pixels[x - 1, y - 1] = nullClr then
begin
tmpRegn := CreateRectRgn(x - 1, y - 1, x, y);
CombineRgn(regn, regn, tmpRegn, RGN_DIFF);
DeleteObject(tmpRegn);
end;
SetWindowRgn(FMain.handle, regn, true);
end;
procedure TFMain.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Draging := true;
x0 := x;
y0 := y;
end;
procedure TFMain.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Draging := false;
end;
procedure TFMain.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Draging then
begin
FMain.Left := FMain.Left + X - X0;
FMain.top := FMain.top + Y - Y0;
end;
end;
procedure TFMain.ImgBtn4Click(Sender: TObject);
begin
FMain.Close;
end;
procedure TFMain.ImgBtn3Click(Sender: TObject);
begin
FMain.Close;
end;
procedure TFMain.ImgBtn2Click(Sender: TObject);
var
WThread: GFilesThread;
begin
Label3.Caption := '';
WThread := GFilesThread.Create(True);
WThread.FreeOnTerminate := True;
WThread.UpdatesUrl := USettings[2];
WThread.ForceCheck := True;
WThread.CreateBackup := StrToInt(USettings[5]);
WThread.LocalRevision := StrToInt(USettings[6]);
WThread.Resume;
end;
procedure TFMain.ImgBtn1Click(Sender: TObject);
begin
RunApp(USettings[0] + Usettings[7]);
FMain.Close;
end;
procedure TFMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
USettings.Free;
end;
procedure TFMain.ImgBtn5Click(Sender: TObject);
begin
FMain.Enabled := False;
Form1.Show;
end;
procedure TFMain.WebBrowser1NavigateComplete2(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
begin
FMain.Panel1.Visible := True;
FMain.Image2.Visible := True;
FMain.Image3.Visible := True;
FMain.Image4.Visible := True;
end;
procedure TFMain.Timer1Timer(Sender: TObject);
var
WThread: GFilesThread;
begin
FMain.Timer1.Enabled := False;
WebBrowser1.Navigate(USettings[1]);
Label3.Caption := '';
WThread := GFilesThread.Create(True);
WThread.FreeOnTerminate := True;
WThread.UpdatesUrl := USettings[2];
WThread.ForceCheck := False;
WThread.CreateBackup := StrToInt(USettings[5]);
WThread.LocalRevision := StrToInt(USettings[6]);
WThread.Resume;
end;
end.

How to create a pan from composite image in Delphi

I'm kind of new to delphi graphics methods and I'm stucked at creating a ... viewport , thats how I call it while i was doing it for a project. I'm sorry I can't provide any code for it but I'm stuck at the logic part , searching google pointed me to some OnPaint , Draw methods. But those are not what I'm trying to accomplish, since I have , for example:
A 1600x1000 background image anchored to the client's top/bottom/right and left.
Multiple TImage elements placed at set x/y coords.
A "hotspot" like a map element in HTML where I can set the clickable areas (for the images i'm placing at step 2)
No zoom needed.
And the most important thing, while the background is dragged, those TImages placed on top of the background need to be dragged too.
My logic (in HTML/jQuery) was to create a #viewportBinder (which was the div i was dragging, transparent bg), followed by another div inside it called #viewtown (1600x1000, the background) which contains the divs (those TImages) placed at set coordinates in CSS.
So when I am dragging the viewportBinder, jQuery sets the new x/y on the #viewport. Implicitly, the divs (TImages) inside the #viewport are moving because the parent was positioned relative.
Does anybody have any experience with this kind of project ? Any snippet of code ?
To be more specific i'll give you my html example of what i accomplised and what i want to port into Delphi code: http://www.youtube.com/watch?v=9iYqzvZFnGA
Sorry if i'm not clear enough, i have no starting point since I have no experience with this in delphi at all. (using RAD Studio 2010)
A very short example how it could be realized in an easy way.
You would use a Paintbox for painting, 1 Backimage, an array of Records with info and transparent pngimages.
Canvas can be manipulated in offset/zoom/rotation.
Moving and hitdetection would happen in mousedown and mousemove.
It's not complete, but might give you an idea how it could be done.
[delphi]
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls,PNGImage, StdCtrls;
type
TBuilding=Record // record for building informations
Pos:TPoint;
PNGImage:TPngImage;
// what ever needed
End;
TBuildingArray=Array of TBuilding; // array of buildings
TForm1 = class(TForm)
PaintBox1: TPaintBox;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure PaintBox1Paint(Sender: TObject);
procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private-Deklarationen }
FXoffs,FYOffs,FZoom:Double; // offset and zoom for painting
FMouseDownPoint:TPoint;
FBackGroundPNG:TPNGImage;
FBuildingArray:TBuildingArray;
procedure Check4Hit(X, Y: Integer);
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
uses Math;
{$R *.dfm}
Procedure SetCanvasZoomAndRotation(ACanvas:TCanvas;Zoom:Double;Angle:Double;CenterpointX,CenterpointY:Double);
var
form : tagXFORM;
Winkel:Double;
begin
Winkel := DegToRad(Angle);
SetGraphicsMode(ACanvas.Handle, GM_ADVANCED);
SetMapMode(ACanvas.Handle,MM_ANISOTROPIC);
form.eM11 := Zoom * cos( Winkel);
form.eM12 := Zoom *Sin( Winkel) ;
form.eM21 := Zoom * (-sin( Winkel));
form.eM22 := Zoom * cos( Winkel) ;
form.eDx := CenterpointX;
form.eDy := CenterpointY;
SetWorldTransform(ACanvas.Handle,form);
end;
Procedure ResetCanvas(ACanvas:TCanvas);
begin
SetCanvasZoomAndRotation(ACanvas , 1, 0, 0,0);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Path:String;
i:Integer;
begin
FZoom := 1;
DoubleBuffered := true;
Path := ExtractFilePath(Paramstr(0));
FBackGroundPNG:=TPNGImage.Create;
FBackGroundPNG.LoadFromFile(Path + 'infect.png');
SetLength(FBuildingArray,3);
for I := 0 to High(FBuildingArray) do
begin
FBuildingArray[i].PNGImage := TPngImage.Create;
FBuildingArray[i].PNGImage.LoadFromFile(Path + Format('B%d.png',[i]));
FBuildingArray[i].Pos.X := I * 300;
FBuildingArray[i].Pos.Y := Random(1000);
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i:Integer;
begin
for I := 0 to High(FBuildingArray) do
begin
FBuildingArray[i].PNGImage.Free;
end;
FBackGroundPNG.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if FZoom=0.5 then FZoom := 1 else FZoom := 0.5;
PaintBox1.Invalidate;
end;
procedure TForm1.Check4Hit(X,Y:Integer);
var
i,Index:Integer;
R:TRect;
P:TPoint;
begin
index := -1;
for I := 0 to High(FBuildingArray) do
begin
R := Rect(FBuildingArray[i].Pos.X,FBuildingArray[i].Pos.Y
,FBuildingArray[i].Pos.X + FBuildingArray[i].PNGImage.Width
,FBuildingArray[i].Pos.Y + FBuildingArray[i].PNGImage.Height);
P := Point(Round((x - FXOffs)/FZoom) ,Round((y - FYOffs)/FZoom));
if PtInRect(R,P) then Index := i;
end;
if index > -1 then
begin
Caption := Format('Last hit %d',[index]);
end
else Caption := 'No Hit';
end;
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Check4Hit(X,Y);
FMouseDownPoint.X := X;
FMouseDownPoint.Y := Y;
end;
procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if ssLeft in Shift then
begin
FXoffs := -( FMouseDownPoint.X - X) ;
FYoffs := -( FMouseDownPoint.Y - Y) ;
if FXoffs>0 then FXoffs := 0;
if FYoffs>0 then FYoffs := 0;
PaintBox1.Invalidate;
end;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
i:Integer;
begin
SetCanvasZoomAndRotation(PaintBox1.Canvas,FZoom,0,FXoffs,FYOffs);
PaintBox1.Canvas.Draw(0,0,FBackGroundPNG);
for I := 0 to High(FBuildingArray) do
begin
PaintBox1.Canvas.Draw(FBuildingArray[i].Pos.X,FBuildingArray[i].Pos.Y,FBuildingArray[i].PNGImage);
end;
end;
end.
[/delphi]
Sorry, but for last several years i working with Lazarus instead of Delphi. But tis article will be informative: http://wiki.lazarus.freepascal.org/Developing_with_Graphics#Create_a_custom_control_which_draws_itself
About relative coordinates nothing to say - it is simple.
About dragging: A long time ago in a galaxy far, far away.. that was something like:
// To start dragging
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
// To stop dragging
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
// To perform dragging
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;

Resources