Clickable line

Hi,

Is there anyone out there who know if it's possible to draw a line on the "canvas" and have it reacting on mouseclick.

Example of the setup is I have five buttons on the form and connect them with lines.
I then want to be able to click on the line and get information on for instance the X and Y points of the endpoints.

If it's possible i also want to assign a "name" to this line to be able to sort wich line is selected if I have for instance 15 lines in a star pattern.

Hoping for this as I have tried myself and failed..
samoneAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

kretzschmarCommented:
? maybe
use tshape(s)
0
Igor UL7AAjrSenior developerCommented:
Hi samone,

here is sample unit. It generates 10 random lines. All lines drawn in OnPaint method.
Moving the mouse (OnMouseMove) over the one of line you can see how cursor changed to crHandPoint.
Clickin on line (OnMouseDown) you can change it's "Selected" property.

Be sure you include "Contnrs" unit in your uses clause.

Enjoy :-)

------
Igor.


unit Unit1;

interface

uses
  Windows, Classes, Graphics, Controls, Forms,
  Contnrs;

type

  TLine = class(TObject)
    A: TPoint;
    B: TPoint;
    Selected: Boolean;
  end;

  TForm1 = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormCreate(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
  public
    Lines: TObjectList;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

// check that point P lie on line with coordinates X1.Y1 - X2.Y2
// Delta - possible distance from the line's point
function PointOnLine(X1,Y1, X2,Y2: Integer; P: TPoint; Delta: Integer): Boolean;
var
  Step: Real;
  R: TRect;
  N: Integer;
  Z: Real;
begin
  Result := False;
  if (Abs(X2-X1) > Abs(Y2-Y1)) and (X2-X1 <> 0) then
  begin
    Step := (Y2- Y1)/abs((X2 - X1));
    if X2 > X1 then N := 1 else N := -1;
    Z := Y1;

    while X1 <> X2 do
    begin
      SetRect(R, X1, Round(Z), X1, Round(Z));
      InflateRect(R, Delta, Delta);
      if PtInRect(R, P) then
      begin
        Result := True;
        Break;
      end;
      Z := Z + Step;
      X1 := X1 + N;
    end;
  end
  else
  if Y2 - Y1 <> 0 then
  begin
    Step := (X2 - X1)/abs((Y2 - Y1));
    if Y2 > Y1 then N := 1 else N := -1;
    Z := X1;

    while Y1 <> Y2 do
    begin
      SetRect(R, Round(Z), Y1, Round(Z), Y1);
      InflateRect(R, Delta, Delta);
      if PtInRect(R, P) then
      begin
        Result := True;
        Break;
      end;
      Z := Z + Step;
      Y1 := Y1 + N;
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
  L: TLine;
begin
  Randomize;
  Lines := TObjectList.Create(true);
  // create 10 random lines
  for I := 1 to 10 do
  begin
    L := TLine.Create;
    L.A.X := Random(Width);
    L.B.X := Random(Width);
    L.A.Y := Random(Height);
    L.B.Y := Random(Height);
    Lines.Add(L);
  end;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  I: Integer;
  L: TLine;
begin
  // draw generated lines
  with Form1.Canvas do
  for I := 0 to Lines.Count - 1 do
  begin
    L := TLine(Lines[I]);
    if L.Selected then
      Pen.Color := clRed
    else
      Pen.Color := clBlack;

    MoveTo(L.A.X, L.A.Y);
    LineTo(L.B.X, L.B.Y);
  end;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  P: TPoint;
  I: Integer;
  L: TLine;
begin
  P.X := X;
  P.Y := Y;
  Cursor := crDefault;
  // detect when mouse over the one of line
  for I := 0 to Lines.Count - 1 do
  begin
    L := TLine(Lines[I]);
    if PointOnLine(L.A.X, L.A.Y, L.B.X, L.B.Y, P, 3) then
      Cursor := crHandPoint;
  end;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  P: TPoint;
  I: Integer;
  L: TLine;
begin
  P.X := X;
  P.Y := Y;
  // select deselect line when mouse clicked on it
  for I := 0 to Lines.Count - 1 do
  begin
    L := TLine(Lines[I]);
    if PointOnLine(L.A.X, L.A.Y, L.B.X, L.B.Y, P, 3) then
    begin
      L.Selected := not L.Selected;
      Invalidate;
    end;
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Lines.Free;
end;

end.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
kretzschmarCommented:
nice igor ;-)
0
JavaScript Best Practices

Save hours in development time and avoid common mistakes by learning the best practices to use for JavaScript.

Igor UL7AAjrSenior developerCommented:
just compilation from another samples :-)
0
geobulCommented:
Hi,
Just another way:

DrawLine adds a line to a StringList with coordinates and name. Label1 shows current line name and coordinates(OnMouseMove).

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure FormPaint(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  LineList : TStringList;

implementation

{$R *.DFM}

const
  Sensitivity = 50;

function Min(X1,X2: integer): integer;
begin
 if X1 <= X2 then result := X1
             else result := X2;
end;

function Max(X1,X2: integer): integer;
begin
 if X1 >= X2 then result := X1
             else result := X2;
end;

procedure DrawLine(X1,Y1,X2,Y2: integer; Name: string);
begin
  with Form1.Canvas do begin
    MoveTo(X1,Y1);
    LineTo(X2,Y2);
  end;
  LineList.Add(Format('%.4d',[X1])+Format('%.4d',[Y1])+Format('%.4d',[X2])+Format('%.4d',[Y2])+Name);
end;

function OverLine(X,Y: integer): integer;
var
  X1,Y1,X2,Y2: integer;
  i: integer;
begin
 result := -1;
 for i := 0 to LineList.Count - 1 do begin
  X1 := StrToInt(Copy(LineList[i],1,4));
  Y1 := StrToInt(Copy(LineList[i],5,4));
  X2 := StrToInt(Copy(LineList[i],9,4));
  Y2 := StrToInt(Copy(LineList[i],13,4));
  if (Y-Y1 <> 0) and (Y-Y1 <> 0) and (X-X2 <> 0) and (X1-X2 <> 0) then begin
   if (X >= Min(X1,X2)) and (X <= Max(X1,X2)) and (Y >= Min(Y1,Y2)) and (Y <= Max(Y1,Y2)) then begin
    if (Round(((X - X1)/(Y - Y1))*Sensitivity) = Round(((X2 - X1)/(Y2 - Y1))*Sensitivity)) or
       (Round(((Y - Y2)/(X - X2))*Sensitivity) = Round(((Y1 - Y2)/(X1 - X2))*Sensitivity)) then begin
      result := i;
      exit;
    end;
   end;
  end;
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  LineList := TStringList.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  LineList.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  DrawLine(5,200,100,5,'Line1');
  DrawLine(5,5,100,200,'Line2');
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
 i: integer;
 s: string;
begin
 i := OverLine(X,Y);
 if i > -1 then begin
  s := LineList[i];
  Label1.Caption := Copy(s,17,Length(s) - 16) + ' ' +
    Copy(s,1,4) + ',' + Copy(s,5,4) + ',' +
    Copy(s,9,4) + ',' + Copy(s,13,4);
  end else Label1.Caption := '';
end;
end.

Regards, Geo
0
manataCommented:
Hi Samone

By now you know how to draw a line on a canvas.

A method to get where you are going would be to use the MoveTo and LineTo methods of the TCanvas Class.

But prior to using this make up coordinates that you may store in an array( ICoordinates : Array[0..10] of Integer;)
this will help you keep track of the starting and ending points of the line.

On your mouse down method you may track the mouse coordinates and compare them to the points in the array.

Facts:
You draw the lines so you know ICoordinates[0] = x Mouse coordinate of the first line, etc

So knowing the order of the lines and the order of the coordinates, the index of the array can work as a tag value of any control.

Good Luck Manata
0
samoneAuthor Commented:
Hi,

I have been sick for a few days and haven't been able too check the answers yet.

I will try to see if and how to use what you have written and I come back with more questions if I don't understand something.

//Ulf
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.