INTELLIGENT WORK FORUMS FOR COMPUTER PROFESSIONALS
Come Join Us!
Are you a Computer / IT professional? Join Tek-Tips now!
- Talk With Other Members
- Be Notified Of Responses
To Your Posts
- Keyword Search
- One-Click Access To Your
Favorite Forums
- Automated Signatures
On Your Posts
- Best Of All, It's Free!
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.
Partner With Us!
"Best Of Breed" Forums Add Stickiness To Your Site

(Download This Button Today!)
Feedback
"...your web site's great! I've been using this system for almost a year now and find it really, really helpful. The people have been helpful in answering just about any question you post in the forums..."
Geography
Where in the world do Tek-Tips members come from?
|
Drawing, moving, and resizing boxes on an image
|
|
I have a little project I'm working on in Delphi 7, and I got to a point where I'm stumped how to continue. The full source to just this project is below, but this project is using a set of components for parsing HTML: http://www.yunqa.de/delphi/doku.php/products/htmlparser/index however, with how I have this, you can ignore and remove these components, and in the procedure where I do the parsing, you can replace it with adding a few fake values (ignoring the HTML all together). That procedure is towards the end (called cmdParseClick). The project's concept is fairly simple, it's an editor for HTML image maps. It consists of two main parts: HTML Editor, and Image Editor. The HTML Editor is only holding the relevant block of HTML code for the map and image, whereas the image editor is showing the image with the ability to draw, move, and resize each 'hotspot' area on the image. The image and the back-end array of hotspot records should be directly synchronized with each other. My problem is figuring out how to recognize when the user is trying to resize one of the boxes on the image. I have it to the point where I can recognize where the user is pointing the mouse on the image, and changing the cursor accordingly. So here's the sequence of what is already working: 1) Load image into back-end image (fImgMain) where original image is always maintained. 2) Copy image to back-end temp image (fImgTemp) where temporary drawing is done to image before display 3) Copy image to TImage component from the temp image to display the image with the drawn areas 4) Paste or write HTML code for an image map (<map>, <area>, and <img> tags involved) 5) Parse HTML and build a virtual list of these 'hotspots' - or "array of THotspot" records 6) Copy main image to temp image 7) Go through the virtual list and draw each box on the temp image 8) Copy temp image with drawn boxes to the image display 9) Recognize when user points mouse over certain areas of image 10) Change cursor of image if mouse is over hotspot (to move), the edge (to resize), or no hotspot (to draw new hotspot) And at this point, how to actually accept user's mouse actions is way over my head. Now when an area is being moved or resized (while mouse is down), the changes on the image need to be updated in the corresponding record in the array. Meaning, as long as user is moving mouse while mouse button is down, any change is recorded in the virtual list of records. After each movement of the mouse in this case, it needs to a) update the record in the array, b) copy main image over to temp image, c) draw new boxes on temp image, and d) copy temp image to image display. At no point shall the main image (fImgMain) ever be changed or drawn to (unless a new image is loaded). Note the type "TMouseDownState" which is a record to recognize what action is to be done while the mouse is down (moving hotspot, resizing left edge, resizing bottom-right edge, etc.). Two important functions are "HotspotAtCursor" and "MouseStateAtCursor" which recognize the position of the mouse over the display image (Img: TImage). The project is simple, just 1 main form only. I tried to put as many comments as possible. Here's the code: CODEunit uMain;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, XPMan, ExtCtrls, Jpeg, ExtDlgs, Menus, Buttons, DIUnicode, DIHtmlParser; const //GripSize = size of resizable border around each hotspot GripSize = 3;
type //Represents one single 'hotspot' or 'area' on the 'map' THotspot = record Area: TRect; URL: String; Shape: String; end;
//Represents current mouse status of a hotspot TMouseHotspot = record Hotspot: THotspot; Active: Bool; DownPos: TPoint; UpPos: TPoint; CurrPos: TPoint; end;
//Primary storage of all hotspot records THotspots = array of THotspot;
//Represents what mouse is doing if mouse button is down TMouseDownState = (msNone, msMove, msSizeW, msSizeN, msSizeE, msSizeS, msSizeNW, msSizeNE, msSizeSW, msSizeSE);
TForm1 = class(TForm) HParser: TDIHtmlParser; dlgOpen: TOpenPictureDialog; pRight: TPanel; Tags: TMemo; Splitter1: TSplitter; pMain: TPanel; pHTML: TPanel; HTML: TMemo; pImage: TPanel; Box: TScrollBox; Img: TImage; Splitter2: TSplitter; MainMenu1: TMainMenu; File1: TMenuItem; New1: TMenuItem; Open1: TMenuItem; Save1: TMenuItem; SaveAs1: TMenuItem; Close1: TMenuItem; N1: TMenuItem; Exit1: TMenuItem; Edit1: TMenuItem; Cut1: TMenuItem; Copy1: TMenuItem; Paste1: TMenuItem; View1: TMenuItem; Image1: TMenuItem; HTML1: TMenuItem; HotspotImage1: TMenuItem; cmdParse: TBitBtn; procedure cmdParseClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ImgMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure ImgMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure ImgMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure Exit1Click(Sender: TObject); procedure Close1Click(Sender: TObject); private fImgMain: TBitmap; fImgTemp: TBitmap; fHotspots: THotspots; fMouseDownState: TMouseDownState; fSelHotspot: Integer; fPosDown: TPoint; function GetHotspot(Index: Integer): THotspot; procedure SetHotspot(Index: Integer; const Value: THotspot); procedure DrawHotspot(const H: THotspot); procedure CodeToImage; procedure ImageToCode; public function LoadFile(const Filename: TFilename): Bool; procedure CloseFile; function HotspotAtCursor: TMouseHotspot; function MouseStateAtCursor: TMouseDownState; property MouseDownState: TMouseDownState read fMouseDownState; property SelHotspot: Integer read fSelHotspot; property Hotspots[Index: Integer]: THotspot read GetHotspot write SetHotspot; end;
var Form1: TForm1;
implementation
{$R *.dfm}
//Draws a single hotspot to the temporary bitmap procedure TForm1.DrawHotspot(const H: THotspot); var C: TCanvas; begin C:= fImgTemp.Canvas; C.Brush.Style:= bsClear; C.Pen.Style:= psSolid; C.Pen.Color:= clGray; C.Pen.Width:= 2; C.Rectangle(H.Area); end;
//Acquires a single hotspot from global list by index function TForm1.GetHotspot(Index: Integer): THotspot; begin Result:= fHotspots[Index]; end;
//Assigns a single hotspot in global list by index procedure TForm1.SetHotspot(Index: Integer; const Value: THotspot); begin fHotspots[Index]:= Value; end;
procedure TForm1.FormCreate(Sender: TObject); begin //Create objects fImgMain:= TBitmap.Create; fImgTemp:= TBitmap.Create; //Set default values SetLength(fHotspots, 0); fSelHotspot:= -1; fMouseDownState:= msNone; //Prepare controls pMain.Align:= alClient; pImage.Align:= alClient; HTML.Align:= alClient; Box.Align:= alClient; //Forcefully show main form Show; BringToFront; Application.ProcessMessages; //Automatically prompt to open an image (not necessary) if dlgOpen.Execute then begin LoadFile(dlgOpen.FileName); end; end;
procedure TForm1.FormDestroy(Sender: TObject); begin //Free objects fImgMain.Free; fImgTemp.Free; end;
//Loads a single image file to be primary image to work with function TForm1.LoadFile(const Filename: TFilename): Bool; var Ext: String; J: TJpegImage; begin Result:= False; if FileExists(Filename) then begin Ext:= LowerCase(ExtractFileExt(Filename)); if (Ext = '.jpg') or (Ext = '.jpeg') then begin J:= TJpegImage.Create; try J.LoadFromFile(Filename); fImgMain.Assign(J); Result:= True; finally J.Free; end; end else if (Ext = '.bmp') then begin fImgMain.LoadFromFile(Filename); Result:= True; end else begin raise Exception.Create('Invalid file format "'+Ext+'"'); end; end; if Result then begin fImgTemp.Assign(fImgMain); Img.Picture.Assign(fImgMain); end; end;
//Closes the currently opened file procedure TForm1.CloseFile; begin
end;
//Detect mouse movement on image procedure TForm1.ImgMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var H: TMouseHotspot; begin //Identify current mouse state case Self.fMouseDownState of msNone: begin //Mouse button is NOT down - change cursor H:= HotspotAtCursor; if H.Active then begin //Mouse is over a hotspot case MouseStateAtCursor of msMove: Img.Cursor:= crSizeAll; msSizeN, msSizeS: Img.Cursor:= crSizeNS; msSizeW, msSizeE: Img.Cursor:= crSizeWE; msSizeNW, msSizeSE: Img.Cursor:= crSizeNWSE; msSizeSW, msSizeNE: Img.Cursor:= crSizeNESW; end; end else begin //Mouse is NOT over a hotspot Img.Cursor:= crCross; end; end; //The following events update Rect values within fHotspots // With these, the mouse button IS down msMove: begin //Mouse is moving selected hotspot
end; msSizeW: begin //Mouse is sizing left side of hotspot
end; msSizeN: begin //Mouse is sizing top side of hotspot
end; msSizeE: begin //Mouse is sizing right side of hotspot
end; msSizeS: begin //Mouse is sizing bottom side of hotspot end; msSizeNW: begin //Mouse is sizing top-left corner of hotspot
end; msSizeNE: begin //Mouse is sizing top-right corner of hotspot
end; msSizeSW: begin //Mouse is sizing bottom-left corner of hotspot
end; msSizeSE: begin //Mouse is sizing bottom-right corner of hotspot end; end; end;
procedure TForm1.ImgMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var H: TMouseHotspot; begin fPosDown:= Point(X, Y); //Record current mouse position for calculations H:= HotspotAtCursor; if H.Active then begin //Mouse clicked on a hotspot fMouseDownState:= MouseStateAtCursor; //Record what mouse is doing //Record currently active hotspot
end else begin //Mouse clicked where there is no hotspot - draw new? end; end; procedure TForm1.ImgMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var I: Integer; H: THotspot; XM, YM: Integer; L: TStringList; begin L:= TStringList.Create; try //Update code with new value(s) for I:= 0 to Length(fHotspots) - 1 do begin H:= fHotspots[I];
L.Append(' <area shape="'+H.Shape+'" coords="'+'" href="'+'" alt="'+'" />');
fHotspots[I]:= H; end; finally L.Free; end;
//Re-parse HTML with new values
//Re-draw hotspots on image
fMouseDownState:= msNone; end;
//Acquires hotspot info at current mouse position // Returns with 'Active = False' if no hotspot is present function TForm1.HotspotAtCursor: TMouseHotspot; var I: Integer; H: THotspot; X, Y: Integer; begin X:= Img.ScreenToClient(Mouse.CursorPos).X; Y:= Img.ScreenToClient(Mouse.CursorPos).Y; Result.Active:= False; for I:= 0 to Length(fHotspots)-1 do begin H:= fHotspots[I]; //Detect if mouse is at an edge of hotspot (to resize) if (X >= H.Area.Left) and (X <= H.Area.Right) and (Y >= H.Area.Top) and (Y <= H.Area.Bottom) then begin Result.Hotspot:= H; Result.Active:= True; Result.DownPos:= fPosDown; Result.CurrPos:= Point(X, Y); Break; end; end; end;
//Acquires mouse down state info at current mouse position // In other words, with mouse where it is now, what would be done if clicked? function TForm1.MouseStateAtCursor: TMouseDownState; var X, Y: Integer; H: TMouseHotspot; begin Result:= msNone; X:= Img.ScreenToClient(Mouse.CursorPos).X; Y:= Img.ScreenToClient(Mouse.CursorPos).Y; H:= Self.HotspotAtCursor; if H.Active then begin //Mouse is over a hotspot if (X >= H.Hotspot.Area.Right - GripSize) and (Y >= H.Hotspot.Area.Bottom - GripSize) then begin Result:= msSizeSE; end else if (X >= H.Hotspot.Area.Right - GripSize) and (Y <= H.Hotspot.Area.Top + GripSize) then begin Result:= msSizeNE; end else if (X <= H.Hotspot.Area.Left + GripSize) and (Y >= H.Hotspot.Area.Bottom - GripSize) then begin Result:= msSizeSW; end else if (X <= H.Hotspot.Area.Left + GripSize) and (Y <= H.Hotspot.Area.Top + GripSize) then begin Result:= msSizeNW; end else if (X <= H.Hotspot.Area.Left + GripSize) then begin Result:= msSizeW; end else if (Y <= H.Hotspot.Area.Top + GripSize) then begin Result:= msSizeN; end else if (X >= H.Hotspot.Area.Right - GripSize) then begin Result:= msSizeE; end else if (Y >= H.Hotspot.Area.Bottom - GripSize) then begin Result:= msSizeS; end else begin //Mouse is directly over hotspot, move, not resize Result:= msMove; end; end; end;
//Exit the application procedure TForm1.Exit1Click(Sender: TObject); begin Close; end;
//Close currently opened image procedure TForm1.Close1Click(Sender: TObject); begin Self.CloseFile; end;
//Main parsing procedure - need to migrate to 'CodeToImage' procedure procedure TForm1.cmdParseClick(Sender: TObject); var X: Integer; H: THotspot; T: String; ID: String; procedure Add(const Hotspot: THotspot); begin SetLength(fHotspots, Length(fHotspots)+1); fHotspots[Length(fHotspots)-1]:= Hotspot; end; begin Tags.Lines.Clear; HParser.SourceBufferAsStr:= HTML.Text; ID:= ''; SetLength(fHotspots, 0); while HParser.ParseNextPiece do begin case HParser.PieceType of ptHTMLTag: begin T:= LowerCase(HParser.HtmlTag.TagName); if T = 'map' then begin ID:= HParser.HtmlTag.ValueOfNameCS['id']; if ID = '' then ID:= HParser.HtmlTag.ValueOfNameCI['name']; if ID <> '' then begin Tags.Lines.Append('Map: "'+ID+'"'); end; end else if T = 'area' then begin H.URL:= HParser.HtmlTag.ValueOfNameCI['href']; H.Shape:= HParser.HtmlTag.ValueOfNameCI['shape']; T:= HParser.HtmlTag.ValueOfNameCI['coords']+','; X:= 0; while Length(T) > 0 do begin if Pos(',', T) > 1 then begin case X of 0: H.Area.Left:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); 1: H.Area.Top:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); 2: H.Area.Right:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); 3: H.Area.Bottom:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); else T:= ''; end; Delete(T, 1, Pos(',', T)); end else begin T:= ''; end; Inc(X); end; Add(H); Tags.Lines.Append(' Area: '+ IntToStr(H.Area.Left)+'x'+IntToStr(H.Area.Top)); end else if T = 'img' then begin Tags.Lines.Append('Image: '+HParser.HtmlTag.ValueOfNameCS['src']); end; end; end; end;
fImgTemp.Assign(fImgMain); for X:= 0 to Length(fHotspots)-1 do begin DrawHotspot(fHotspots[X]); end; Img.Picture.Assign(fImgTemp); end;
//Converts HTML code to list of hotspots, then draws image procedure TForm1.CodeToImage; begin
end;
//Converts list of hotspots (updated by image) to HTML code procedure TForm1.ImageToCode; begin
end;
end. And here's the DFM (form) code: CODEobject Form1: TForm1 Left = 407 Top = 245 Width = 810 Height = 779 Caption = 'Image Map Editor' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] Menu = MainMenu1 OldCreateOrder = False Position = poScreenCenter OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 object Splitter1: TSplitter Left = 611 Top = 0 Width = 5 Height = 721 Align = alRight end object pRight: TPanel Left = 616 Top = 0 Width = 178 Height = 721 Align = alRight TabOrder = 0 DesignSize = ( 178 721) object Tags: TMemo Left = 6 Top = 16 Width = 166 Height = 666 Anchors = [akLeft, akTop, akRight, akBottom] TabOrder = 0 end object cmdParse: TBitBtn Left = 8 Top = 688 Width = 163 Height = 25 Anchors = [akLeft, akRight, akBottom] Caption = 'Parse' TabOrder = 1 OnClick = cmdParseClick end end object pMain: TPanel Left = 0 Top = 0 Width = 569 Height = 721 Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] TabOrder = 1 object Splitter2: TSplitter Left = 1 Top = 225 Width = 567 Height = 5 Cursor = crVSplit Align = alTop end object pHTML: TPanel Left = 1 Top = 1 Width = 567 Height = 224 Align = alTop TabOrder = 0 object HTML: TMemo Left = 1 Top = 1 Width = 510 Height = 222 Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -13 Font.Name = 'Consolas' Font.Style = [] Lines.Strings = ( '<MAP NAME="bm10">' ' <AREA SHAPE="RECT" COORDS="8,29,55,48" HREF="the_list_tab.htm"' + '>' ' <AREA SHAPE="RECT" COORDS="56,29,111,48" HREF="the_general_tab' + '.htm">' ' <AREA SHAPE="RECT" COORDS="112,29,198,47" HREF="the_general_de' + 'fault_tab.htm">' ' <AREA SHAPE="RECT" COORDS="200,29,257,47" HREF="the_price_setu' + 'p_tab.htm">' ' <AREA SHAPE="RECT" COORDS="258,30,345,47" HREF="the_customer_p' + 'rice_setup_tab.htm">' ' <AREA SHAPE="RECT" COORDS="346,28,421,48" HREF="the_measuremen' + 't_tab.htm">' ' <AREA SHAPE="RECT" COORDS="422,29,471,49" HREF="reports_tab.ht' + 'm">' ' <AREA SHAPE="RECT" COORDS="12,52,134,72" HREF="hs204.htm">' ' <AREA SHAPE="RECT" COORDS="134,52,251,71" HREF="hs205.htm">' ' <AREA SHAPE="RECT" COORDS="324,89,534,232" HREF="purchase_orde' + 'r_view_summary_tab.htm">' ' <AREA SHAPE="RECT" COORDS="82,112,159,124" HREF="purchase_orde' + 'rs_vendor_invoice.htm">' ' <AREA SHAPE="RECT" COORDS="69,128,110,144" HREF="inventory_bat' + 'ch_transfer.htm">' ' <AREA SHAPE="RECT" COORDS="471,31,536,50" HREF="hs219.htm">' '</MAP>' '<IMG border=0 src="../images/bm10.png" isMap width=571 height =4' + '51 useMap=#bm10 >') ParentFont = False ScrollBars = ssBoth TabOrder = 0 WordWrap = False end end object pImage: TPanel Left = 1 Top = 272 Width = 567 Height = 448 Align = alBottom Anchors = [akLeft, akTop, akRight, akBottom] TabOrder = 1 object Box: TScrollBox Left = 1 Top = 1 Width = 510 Height = 446 HorzScrollBar.Smooth = True HorzScrollBar.Tracking = True VertScrollBar.Smooth = True VertScrollBar.Tracking = True Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] Color = clWhite ParentColor = False TabOrder = 0 object Img: TImage Left = 0 Top = 0 Width = 200 Height = 500 Cursor = crSizeNWSE AutoSize = True IncrementalDisplay = True OnMouseDown = ImgMouseDown OnMouseMove = ImgMouseMove OnMouseUp = ImgMouseUp end end end end object HParser: TDIHtmlParser FilterHtmlTags.StartTags = fiShow Left = 184 Top = 56 end object dlgOpen: TOpenPictureDialog Left = 216 Top = 88 end object MainMenu1: TMainMenu Left = 185 Top = 89 object File1: TMenuItem Caption = 'File' object New1: TMenuItem Caption = 'New...' end object Open1: TMenuItem Caption = 'Open' object Image1: TMenuItem Caption = 'Image...' end object HTML1: TMenuItem Caption = 'HTML...' end object HotspotImage1: TMenuItem Caption = 'Mapped Image...' end end object Save1: TMenuItem Caption = 'Save' end object SaveAs1: TMenuItem Caption = 'Save As...' end object Close1: TMenuItem Caption = 'Close' OnClick = Close1Click end object N1: TMenuItem Caption = '-' end object Exit1: TMenuItem Caption = 'Exit' OnClick = Exit1Click end end object Edit1: TMenuItem Caption = 'Edit' object Cut1: TMenuItem Caption = 'Cut' end object Copy1: TMenuItem Caption = 'Copy' end object Paste1: TMenuItem Caption = 'Paste' end end object View1: TMenuItem Caption = 'View' end end end JD Solutions |
|
I did it! Here's the new source below: CODEunit uMain;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, XPMan, ExtCtrls, Jpeg, ExtDlgs, Menus, Buttons, DIUnicode, DIHtmlParser, ComCtrls; const //GripSize = size of resizable border around each hotspot GripSize = 2;
type //Represents what mouse is doing if mouse button is down TMouseDownState = (msNone, msMove, msSizeW, msSizeN, msSizeE, msSizeS, msSizeNW, msSizeNE, msSizeSW, msSizeSE);
TStatPan = (spStatus, spPosition, spSize, spModify, spFilename);
//Represents one single 'hotspot' or 'area' on the 'map' THotspot = record Area: TRect; URL: String; Shape: String; end;
//Represents current mouse status of a hotspot TMouseHotspot = record Hotspot: THotspot; Active: Bool; DownPos: TPoint; UpPos: TPoint; CurrPos: TPoint; Index: Integer; end;
//Primary storage of all hotspot records THotspots = array of THotspot;
TForm1 = class(TForm) HParser: TDIHtmlParser; dlgOpen: TOpenPictureDialog; pMain: TPanel; pHTML: TPanel; HTML: TMemo; pImage: TPanel; Box: TScrollBox; Img: TImage; Splitter2: TSplitter; MainMenu1: TMainMenu; File1: TMenuItem; Open1: TMenuItem; Save1: TMenuItem; SaveAs1: TMenuItem; Close1: TMenuItem; N1: TMenuItem; Exit1: TMenuItem; Edit1: TMenuItem; Cut1: TMenuItem; Copy1: TMenuItem; Paste1: TMenuItem; View1: TMenuItem; Image1: TMenuItem; HotspotImage1: TMenuItem; Stat: TStatusBar; dlgOpenHTML: TOpenDialog; Panel1: TPanel; cmdParse: TBitBtn; Label1: TLabel; txtLeft: TEdit; udLeft: TUpDown; Label2: TLabel; txtTop: TEdit; udTop: TUpDown; Label3: TLabel; txtWidth: TEdit; udWidth: TUpDown; txtHeight: TEdit; Label4: TLabel; udHeight: TUpDown; Label5: TLabel; txtURL: TEdit; procedure cmdParseClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ImgMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure ImgMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure ImgMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure Exit1Click(Sender: TObject); procedure Close1Click(Sender: TObject); procedure Image1Click(Sender: TObject); procedure HTMLChange(Sender: TObject); procedure HotspotImage1Click(Sender: TObject); procedure txtLeftChange(Sender: TObject); procedure udLeftClick(Sender: TObject; Button: TUDBtnType); private fChanging: Bool; fImgMain: TBitmap; fImgTemp: TBitmap; fHotspots: THotspots; fMouseDownState: TMouseDownState; fSelHotspot: Integer; fStartHotspot: THotspot; fPosDown: TPoint; function GetHotspot(Index: Integer): THotspot; procedure SetHotspot(Index: Integer; const Value: THotspot); procedure DrawHotspot(const H: THotspot; const Selected: Bool); procedure CodeToImage; procedure ImageToCode; function GetStatus(Pan: TStatPan): String; procedure SetStatus(Pan: TStatPan; const Value: String); procedure HotspotSelected(const Hotspot: THotspot); procedure HotspotUnselected; procedure HotspotEdit(const DoEdit: Bool); procedure ChangeSelHotspot; procedure DrawHotspots; public function LoadFile(const Filename: TFilename): Bool; procedure CloseFile; function HotspotAtCursor: TMouseHotspot; function MouseStateAtCursor: TMouseDownState; property MouseDownState: TMouseDownState read fMouseDownState; property SelHotspot: Integer read fSelHotspot; property Hotspots[Index: Integer]: THotspot read GetHotspot write SetHotspot; property Status[Pan: TStatPan]: String read GetStatus write SetStatus; end;
var Form1: TForm1;
implementation
{$R *.dfm}
//Draws a single hotspot to the temporary bitmap procedure TForm1.DrawHotspot(const H: THotspot; const Selected: Bool); var C: TCanvas; begin C:= fImgTemp.Canvas; C.Brush.Style:= bsClear; C.Pen.Style:= psSolid; C.Pen.Width:= GripSize; if Selected then C.Pen.Color:= clBlue else C.Pen.Color:= clGray; C.Rectangle(H.Area); end;
//Draws all hotspots to the temporary bitmap - then copies to display procedure TForm1.DrawHotspots; var X: Integer; begin fImgTemp.Assign(fImgMain); for X:= 0 to Length(fHotspots)-1 do begin if X = fSelHotspot then DrawHotspot(fHotspots[X], True) else DrawHotspot(fHotspots[X], False); end; Img.Picture.Assign(fImgTemp); end;
//Acquires a single hotspot from global list by index function TForm1.GetHotspot(Index: Integer): THotspot; begin Result:= fHotspots[Index]; end;
//Assigns a single hotspot in global list by index procedure TForm1.SetHotspot(Index: Integer; const Value: THotspot); begin fHotspots[Index]:= Value; end;
procedure TForm1.FormCreate(Sender: TObject); begin //Create objects fImgMain:= TBitmap.Create; fImgTemp:= TBitmap.Create; //Set default values SetLength(fHotspots, 0); fSelHotspot:= -1; fMouseDownState:= msNone; fChanging:= False; //Prepare controls Status[spStatus]:= 'Loading...'; pMain.Align:= alClient; pImage.Align:= alClient; HTML.Align:= alClient; Box.Align:= alClient; CloseFile; Show; BringToFront; Application.ProcessMessages; //Done loading Status[spStatus]:= 'Ready'; end;
procedure TForm1.FormDestroy(Sender: TObject); begin //Free objects fImgMain.Free; fImgTemp.Free; end;
//Loads a single image file to be primary image to work with function TForm1.LoadFile(const Filename: TFilename): Bool; var Ext: String; J: TJpegImage; begin Result:= False; CloseFile; if FileExists(Filename) then begin Ext:= LowerCase(ExtractFileExt(Filename)); if (Ext = '.jpg') or (Ext = '.jpeg') then begin J:= TJpegImage.Create; try J.LoadFromFile(Filename); fImgMain.Assign(J); Result:= True; finally J.Free; end; end else if (Ext = '.bmp') then begin fImgMain.LoadFromFile(Filename); Result:= True; end else begin raise Exception.Create('Invalid file format "'+Ext+'"'); end; end; if Result then begin fImgTemp.Assign(fImgMain); Img.Picture.Assign(fImgMain); Status[spFilename]:= dlgOpen.FileName; end; end;
//Closes the currently opened file procedure TForm1.CloseFile; begin Status[spFilename]:= '[No Image Open]'; HotspotUnselected; end;
//Detect mouse movement on image procedure TForm1.ImgMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var H: TMouseHotspot; HS: THotspot; begin //Identify current mouse state if fSelHotspot >= 0 then HS:= fHotspots[fSelHotspot]; Status[spPosition]:= IntToStr(X) + ' x ' + IntToStr(Y); case fMouseDownState of msNone: begin //Mouse button is NOT down - change cursor H:= HotspotAtCursor; if H.Active then begin //Mouse is over a hotspot case MouseStateAtCursor of msMove: Img.Cursor:= crSizeAll; msSizeN, msSizeS: Img.Cursor:= crSizeNS; msSizeW, msSizeE: Img.Cursor:= crSizeWE; msSizeNW, msSizeSE: Img.Cursor:= crSizeNWSE; msSizeSW, msSizeNE: Img.Cursor:= crSizeNESW; end; end else begin //Mouse is NOT over a hotspot Img.Cursor:= crCross; end; end; //The following events update Rect values within fHotspots // With these, the mouse button IS down msMove: begin //Mouse is moving selected hotspot HS.Area.Left:= fStartHotspot.Area.Left + (X - fPosDown.X); HS.Area.Top:= fStartHotspot.Area.Top + (Y - fPosDown.Y); HS.Area.Right:= fStartHotspot.Area.Right + (X - fPosDown.X); HS.Area.Bottom:= fStartHotspot.Area.Bottom + (Y - fPosDown.Y); end; msSizeW: begin //Mouse is sizing left side of hotspot HS.Area.Left:= fStartHotspot.Area.Left + (X - fPosDown.X); end; msSizeN: begin //Mouse is sizing top side of hotspot HS.Area.Top:= fStartHotspot.Area.Top + (Y - fPosDown.Y); end; msSizeE: begin //Mouse is sizing right side of hotspot HS.Area.Right:= fStartHotspot.Area.Right + (X - fPosDown.X); end; msSizeS: begin //Mouse is sizing bottom side of hotspot HS.Area.Bottom:= fStartHotspot.Area.Bottom + (Y - fPosDown.Y); end; msSizeNW: begin //Mouse is sizing top-left corner of hotspot HS.Area.Left:= fStartHotspot.Area.Left + (X - fPosDown.X); HS.Area.Top:= fStartHotspot.Area.Top + (Y - fPosDown.Y); end; msSizeNE: begin //Mouse is sizing top-right corner of hotspot HS.Area.Top:= fStartHotspot.Area.Top + (Y - fPosDown.Y); HS.Area.Right:= fStartHotspot.Area.Right + (X - fPosDown.X); end; msSizeSW: begin //Mouse is sizing bottom-left corner of hotspot HS.Area.Bottom:= fStartHotspot.Area.Bottom + (Y - fPosDown.Y); HS.Area.Left:= fStartHotspot.Area.Left + (X - fPosDown.X); end; msSizeSE: begin //Mouse is sizing bottom-right corner of hotspot HS.Area.Bottom:= fStartHotspot.Area.Bottom + (Y - fPosDown.Y); HS.Area.Right:= fStartHotspot.Area.Right + (X - fPosDown.X); end; end; if fMouseDownState <> msNone then begin fHotspots[fSelHotspot]:= HS; HotspotSelected(HS); DrawHotspots; end; end;
procedure TForm1.ImgMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var H: TMouseHotspot; begin fChanging:= True; fPosDown:= Point(X, Y); //Record current mouse position for calculations H:= HotspotAtCursor; if H.Active then begin //Mouse clicked on a hotspot fMouseDownState:= MouseStateAtCursor; //Record what mouse is doing fSelHotspot:= H.Index; fStartHotspot:= H.Hotspot; HotspotSelected(H.Hotspot); end else begin //Mouse clicked where there is no hotspot - draw new? fSelHotspot:= -1; HotspotUnselected; end; end; procedure TForm1.ImgMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var I: Integer; H: THotspot; XM, YM: Integer; L: TStringList; begin
L:= TStringList.Create; try //Update code with new value(s) for I:= 0 to Length(fHotspots) - 1 do begin H:= fHotspots[I];
L.Append(' <area shape="'+H.Shape+'" coords="'+'" href="'+'" alt="'+'" />');
fHotspots[I]:= H; end; finally L.Free; end;
//Re-parse HTML with new values
//Re-draw hotspots on image fChanging:= False; fMouseDownState:= msNone; DrawHotspots; end;
//Acquires hotspot info at current mouse position // Returns with 'Active = False' if no hotspot is present function TForm1.HotspotAtCursor: TMouseHotspot; var I: Integer; H: THotspot; X, Y: Integer; begin X:= Img.ScreenToClient(Mouse.CursorPos).X; Y:= Img.ScreenToClient(Mouse.CursorPos).Y; Result.Active:= False; for I:= 0 to Length(fHotspots)-1 do begin H:= fHotspots[I]; //Detect if mouse is at an edge of hotspot (to resize) if (X >= H.Area.Left) and (X <= H.Area.Right) and (Y >= H.Area.Top) and (Y <= H.Area.Bottom) then begin Result.Hotspot:= H; Result.Active:= True; Result.DownPos:= fPosDown; Result.CurrPos:= Point(X, Y); Result.Index:= I; Break; end; end; end;
//Acquires mouse down state info at current mouse position // In other words, with mouse where it is now, what would be done if clicked? function TForm1.MouseStateAtCursor: TMouseDownState; var X, Y: Integer; H: TMouseHotspot; begin Result:= msNone; X:= Img.ScreenToClient(Mouse.CursorPos).X; Y:= Img.ScreenToClient(Mouse.CursorPos).Y; H:= Self.HotspotAtCursor; if H.Active then begin //Mouse is over a hotspot if (X >= H.Hotspot.Area.Right - GripSize) and (Y >= H.Hotspot.Area.Bottom - GripSize) then begin Result:= msSizeSE; end else if (X >= H.Hotspot.Area.Right - GripSize) and (Y <= H.Hotspot.Area.Top + GripSize) then begin Result:= msSizeNE; end else if (X <= H.Hotspot.Area.Left + GripSize) and (Y >= H.Hotspot.Area.Bottom - GripSize) then begin Result:= msSizeSW; end else if (X <= H.Hotspot.Area.Left + GripSize) and (Y <= H.Hotspot.Area.Top + GripSize) then begin Result:= msSizeNW; end else if (X <= H.Hotspot.Area.Left + GripSize) then begin Result:= msSizeW; end else if (Y <= H.Hotspot.Area.Top + GripSize) then begin Result:= msSizeN; end else if (X >= H.Hotspot.Area.Right - GripSize) then begin Result:= msSizeE; end else if (Y >= H.Hotspot.Area.Bottom - GripSize) then begin Result:= msSizeS; end else begin //Mouse is directly over hotspot, move, not resize Result:= msMove; end; end; end;
//Exit the application procedure TForm1.Exit1Click(Sender: TObject); begin Close; end;
//Close currently opened image procedure TForm1.Close1Click(Sender: TObject); begin Self.CloseFile; end;
//Main parsing procedure - need to migrate to 'CodeToImage' procedure procedure TForm1.cmdParseClick(Sender: TObject); var X: Integer; H: THotspot; T: String; ID: String; procedure Add(const Hotspot: THotspot); begin SetLength(fHotspots, Length(fHotspots)+1); fHotspots[Length(fHotspots)-1]:= Hotspot; end; begin Status[spModify]:= ''; cmdParse.Enabled:= False; HotspotUnselected; HParser.SourceBufferAsStr:= HTML.Text; ID:= ''; SetLength(fHotspots, 0); while HParser.ParseNextPiece do begin if HParser.PieceType = ptHTMLTag then begin T:= LowerCase(HParser.HtmlTag.TagName); if T = 'map' then begin ID:= HParser.HtmlTag.ValueOfNameCS['id']; if ID = '' then begin ID:= HParser.HtmlTag.ValueOfNameCI['name']; end; end else if T = 'area' then begin H.URL:= HParser.HtmlTag.ValueOfNameCI['href']; H.Shape:= HParser.HtmlTag.ValueOfNameCI['shape']; T:= HParser.HtmlTag.ValueOfNameCI['coords']+','; X:= 0; while Length(T) > 0 do begin if Pos(',', T) > 1 then begin case X of 0: H.Area.Left:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); 1: H.Area.Top:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); 2: H.Area.Right:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); 3: H.Area.Bottom:= StrToIntDef(Copy(T, 1, Pos(',', T)-1), 0); else T:= ''; end; Delete(T, 1, Pos(',', T)); end else begin T:= ''; end; //if pos... Inc(X); end; //while length... Add(H); end; //if t = ... end; //if hparser.piecetype... end; //while hparser...
DrawHotspots;
end;
//Converts HTML code to list of hotspots, then draws image procedure TForm1.CodeToImage; begin
end;
//Converts list of hotspots (updated by image) to HTML code procedure TForm1.ImageToCode; begin
end;
procedure TForm1.Image1Click(Sender: TObject); begin if dlgOpen.Execute then begin Self.CloseFile; Self.LoadFile(dlgOpen.FileName); cmdParseClick(Self); end; end;
function TForm1.GetStatus(Pan: TStatPan): String; begin case Pan of spStatus: Result:= Stat.Panels[0].Text; spPosition: Result:= Stat.Panels[1].Text; spSize: Result:= Stat.Panels[2].Text; spModify: Result:= Stat.Panels[3].Text; spFilename: Result:= Stat.Panels[4].Text; end; end;
procedure TForm1.SetStatus(Pan: TStatPan; const Value: String); begin case Pan of spStatus: Stat.Panels[0].Text:= Value; spPosition: Stat.Panels[1].Text:= Value; spSize: Stat.Panels[2].Text:= Value; spModify: Stat.Panels[3].Text:= Value; spFilename: Stat.Panels[4].Text:= Value; end; end;
procedure TForm1.HTMLChange(Sender: TObject); begin cmdParse.Enabled:= True; Status[spModify]:= 'Modified'; end;
procedure TForm1.HotspotImage1Click(Sender: TObject); begin if dlgOpenHTML.Execute then begin try HTML.Lines.LoadFromFile(dlgOpenHTML.FileName); cmdParseClick(Self); except on e: exception do begin raise Exception.Create( 'Invalid HTML or text in file "'+dlgOpenHTML.FileName+'"'); end; end; end; end;
procedure TForm1.HotspotSelected(const Hotspot: THotspot); begin udLeft.Position:= Hotspot.Area.Left; udTop.Position:= Hotspot.Area.Top; udWidth.Position:= Hotspot.Area.Right - Hotspot.Area.Left; udHeight.Position:= Hotspot.Area.Bottom - Hotspot.Area.Top; txtURL.Text:= Hotspot.URL; if not fChanging then HotspotEdit(True); end;
procedure TForm1.HotspotUnselected; begin udLeft.Position:= 0; udTop.Position:= 0; udWidth.Position:= 0; udHeight.Position:= 0; txtURL.Clear; HotspotEdit(False); end;
procedure TForm1.HotspotEdit(const DoEdit: Bool); begin txtLeft.Enabled:= DoEdit; txtTop.Enabled:= DoEdit; txtWidth.Enabled:= DoEdit; txtHeight.Enabled:= DoEdit; udLeft.Enabled:= DoEdit; udTop.Enabled:= DoEdit; udWidth.Enabled:= DoEdit; udHeight.Enabled:= DoEdit; txtURL.Enabled:= DoEdit; end;
procedure TForm1.ChangeSelHotspot; var H: THotspot; begin //Read values from user entry and set records if not fChanging then begin if fSelHotspot >= 0 then begin H:= fHotspots[fSelHotspot];
H.Area.Left:= udLeft.Position; H.Area.Top:= udTop.Position; H.Area.Right:= udLeft.Position + udWidth.Position; H.Area.Bottom:= udTop.Position + udHeight.Position; H.URL:= txtURL.Text;
fHotspots[fSelHotspot]:= H; end; //Update HTML
//Parse HTML DrawHotspots; end; end;
procedure TForm1.txtLeftChange(Sender: TObject); begin ChangeSelHotspot; end;
procedure TForm1.udLeftClick(Sender: TObject; Button: TUDBtnType); begin ChangeSelHotspot; end;
end. And the DFM: CODEobject Form1: TForm1 Left = 343 Top = 250 Width = 774 Height = 778 Caption = 'Image Map Editor' Color = 16117483 Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] Menu = MainMenu1 OldCreateOrder = False Position = poScreenCenter OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 object pMain: TPanel Left = 0 Top = 0 Width = 721 Height = 701 Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] BevelOuter = bvNone ParentBackground = True ParentColor = True TabOrder = 0 object Splitter2: TSplitter Left = 0 Top = 193 Width = 721 Height = 5 Cursor = crVSplit Align = alTop Beveled = True Color = clBtnFace ParentColor = False ResizeStyle = rsUpdate end object pHTML: TPanel Left = 0 Top = 0 Width = 721 Height = 193 Align = alTop BevelOuter = bvNone TabOrder = 0 object HTML: TMemo Left = 0 Top = 0 Width = 664 Height = 193 Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -13 Font.Name = 'Consolas' Font.Style = [] ParentFont = False ScrollBars = ssBoth TabOrder = 0 WordWrap = False OnChange = HTMLChange end end object pImage: TPanel Left = 0 Top = 264 Width = 721 Height = 437 Align = alBottom Anchors = [akLeft, akTop, akRight, akBottom] BevelOuter = bvNone TabOrder = 1 object Box: TScrollBox Left = 0 Top = 41 Width = 664 Height = 396 HorzScrollBar.Smooth = True HorzScrollBar.Tracking = True VertScrollBar.Smooth = True VertScrollBar.Tracking = True Align = alLeft Anchors = [akLeft, akTop, akRight, akBottom] Color = clWhite ParentColor = False TabOrder = 0 object Img: TImage Left = 0 Top = 0 Width = 200 Height = 200 Cursor = crSizeNWSE AutoSize = True IncrementalDisplay = True OnMouseDown = ImgMouseDown OnMouseMove = ImgMouseMove OnMouseUp = ImgMouseUp end end object Panel1: TPanel Left = 0 Top = 0 Width = 721 Height = 41 Align = alTop ParentBackground = False TabOrder = 1 DesignSize = ( 721 41) object Label1: TLabel Left = 128 Top = 4 Width = 23 Height = 13 Caption = 'Left' FocusControl = txtLeft Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] ParentFont = False Transparent = True end object Label2: TLabel Left = 208 Top = 4 Width = 23 Height = 13 Caption = 'Top' FocusControl = txtTop Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] ParentFont = False Transparent = True end object Label3: TLabel Left = 296 Top = 4 Width = 34 Height = 13 Caption = 'Width' FocusControl = txtWidth Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] ParentFont = False Transparent = True end object Label4: TLabel Left = 376 Top = 4 Width = 38 Height = 13 Caption = 'Height' FocusControl = txtHeight Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] ParentFont = False Transparent = True end object Label5: TLabel Left = 464 Top = 4 Width = 26 Height = 13 Caption = 'URL' FocusControl = txtURL Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [fsBold] ParentFont = False Transparent = True end object cmdParse: TBitBtn Left = 8 Top = 5 Width = 113 Height = 32 Caption = 'Synchronize' Enabled = False TabOrder = 0 OnClick = cmdParseClick end object txtLeft: TEdit Left = 128 Top = 16 Width = 53 Height = 21 Cursor = crHandPoint TabOrder = 1 Text = '0' OnChange = txtLeftChange end object udLeft: TUpDown Left = 181 Top = 16 Width = 16 Height = 21 Cursor = crHandPoint Associate = txtLeft Min = -9999 Max = 9999 TabOrder = 2 OnClick = udLeftClick end object txtTop: TEdit Left = 208 Top = 16 Width = 53 Height = 21 Cursor = crHandPoint TabOrder = 3 Text = '0' OnChange = txtLeftChange end object udTop: TUpDown Left = 261 Top = 16 Width = 16 Height = 21 Cursor = crHandPoint Associate = txtTop Min = -9999 Max = 9999 TabOrder = 4 OnClick = udLeftClick end object txtWidth: TEdit Left = 296 Top = 16 Width = 53 Height = 21 Cursor = crHandPoint TabOrder = 5 Text = '0' OnChange = txtLeftChange end object udWidth: TUpDown Left = 349 Top = 16 Width = 16 Height = 21 Cursor = crHandPoint Associate = txtWidth Min = -9999 Max = 9999 TabOrder = 6 OnClick = udLeftClick end object txtHeight: TEdit Left = 376 Top = 16 Width = 53 Height = 21 Cursor = crHandPoint TabOrder = 7 Text = '0' OnChange = txtLeftChange end object udHeight: TUpDown Left = 429 Top = 16 Width = 16 Height = 21 Cursor = crHandPoint Associate = txtHeight Min = -9999 Max = 9999 TabOrder = 8 OnClick = udLeftClick end object txtURL: TEdit Left = 464 Top = 16 Width = 249 Height = 21 Anchors = [akLeft, akTop, akRight] TabOrder = 9 Text = 'www.somewebsite.com' OnChange = txtLeftChange end end end end object Stat: TStatusBar Left = 0 Top = 701 Width = 758 Height = 19 Panels = < item Text = 'Not Ready' Width = 110 end item Text = '0 x 0' Width = 80 end item Text = '0 x 0' Width = 80 end item Width = 80 end item Text = '[No Image Open]' Width = 80 end> end object HParser: TDIHtmlParser FilterHtmlTags.StartTags = fiShow Left = 184 Top = 56 end object dlgOpen: TOpenPictureDialog Left = 216 Top = 88 end object MainMenu1: TMainMenu Left = 185 Top = 89 object File1: TMenuItem Caption = 'File' object Open1: TMenuItem Caption = 'Open' object Image1: TMenuItem Caption = 'Image...' OnClick = Image1Click end object HotspotImage1: TMenuItem Caption = 'Map...' OnClick = HotspotImage1Click end end object Save1: TMenuItem Caption = 'Save' end object SaveAs1: TMenuItem Caption = 'Save As...' end object Close1: TMenuItem Caption = 'Close' OnClick = Close1Click end object N1: TMenuItem Caption = '-' end object Exit1: TMenuItem Caption = 'Exit' OnClick = Exit1Click end end object Edit1: TMenuItem Caption = 'Edit' object Cut1: TMenuItem Caption = 'Cut' end object Copy1: TMenuItem Caption = 'Copy' end object Paste1: TMenuItem Caption = 'Paste' end end object View1: TMenuItem Caption = 'View' end end object dlgOpenHTML: TOpenDialog Filter = 'HTML Image Map Files (*.hmp)|*.hmp|HTML Files (*.htm;*.html)|*.h' + 'tm;*.html|All Files (*.*)|*.*' Left = 248 Top = 88 end end JD Solutions |
|
|
 |
|