Behind the connection - Test
Welcome to the blog of François Piette.
mercredi 24 juillet 2013
lundi 17 juin 2013
Drag And Drop from Windows Explorer
This article presents the required code to handle drag& drop of images from Windows Explorer to your Delphi application. The demo code shows how to drop images on a TListView and to drag & drop from TListView to a TImage.
The code is fairly basic and made so that it can be clearly understood and applied to other types of controls.
Drag & Drop from windows Explorer is handled by an application by registering a window handle along with an instance of an IDropTarget interface.
To make to code easy to reuse, I encapsulated the IDropTarget implementation into a class named TDropTarget and expose the features the Delphi way: using event.
To allow Drag & Drop from Windows Explorer to one of your form, you have to create an instance of TDropTarget and call his Register method passing the form’s handle. Of course you have to assign the events to handler in your form. The events handle all drag and drop operation:
DropAllowed event is called once when the dragged files are entering the area of the registered window. The event handler must set the “Allowed” var argument to TRUE if dropping the file(s) is allowed at the given point.
DragOver event is called as mouse move above the registered window. The event handler must set the “Allowed” var argument to TRUE if dropping the file(s) is allowed at the given point.
Drop event is called when the user drops the files.
DragLeave event is called when the dragged files leave the registered window area.
I could have made a component of TDropTarget but I didn’t. It is a simple object deriving from Object. As it implements an interface IDroptarget, beside the methods of this interface, the object also has to handle _AddRef, _Release and QueryInterface methods which exist in all interfaces. Here we use TObject life cycle, so those methods are simply do-nothing methods.
The code required in your form include creating the object in the form’s contructor (or FormCreate event), assign the event handlers and call register. And of course free the object instance in the destructor:
The demo application doesn’t show image in real size in TListView. Rather, it creates a thumbnail which is displayed in the list view. The thumbnails are stored on disk in the same folder as the original image and are only created if it doesn’t exist yet, or if the original image has been modified. Storing the thumbnail on disk could be a problem in some application because it requires write permission. In my application (Well the application I extracted this code from), it is an advantage because the images are very large and it takes time to create the thumbnails. Keeping the thumbnails on disk improve performance.
Thumbnails are created using GDI+ (See my other blog article about it: http://francois-piette.blogspot.be/2013/05/opensource-gdi-library.html). The code is really easy:
A last note about the demo application: I used custom draw of the ListView items so that it looks exactly how I require it. All list view items are represented by a class named TImageListViewItem. I have selected this representation because in the real application this demo is extracted from, there is a lot of information about each image. The class is really handy to hold the information and the processing related to it.
Here after is the complete source code. There are mainly two files: DropHanlder.pas and DragDropMain.pas. You can also download a zip file with the complete project. See my website at: http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html
Download source code from: http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
The code is fairly basic and made so that it can be clearly understood and applied to other types of controls.
Drag & Drop from windows Explorer is handled by an application by registering a window handle along with an instance of an IDropTarget interface.
To make to code easy to reuse, I encapsulated the IDropTarget implementation into a class named TDropTarget and expose the features the Delphi way: using event.
To allow Drag & Drop from Windows Explorer to one of your form, you have to create an instance of TDropTarget and call his Register method passing the form’s handle. Of course you have to assign the events to handler in your form. The events handle all drag and drop operation:
DropAllowed event is called once when the dragged files are entering the area of the registered window. The event handler must set the “Allowed” var argument to TRUE if dropping the file(s) is allowed at the given point.
DragOver event is called as mouse move above the registered window. The event handler must set the “Allowed” var argument to TRUE if dropping the file(s) is allowed at the given point.
Drop event is called when the user drops the files.
DragLeave event is called when the dragged files leave the registered window area.
I could have made a component of TDropTarget but I didn’t. It is a simple object deriving from Object. As it implements an interface IDroptarget, beside the methods of this interface, the object also has to handle _AddRef, _Release and QueryInterface methods which exist in all interfaces. Here we use TObject life cycle, so those methods are simply do-nothing methods.
The code required in your form include creating the object in the form’s contructor (or FormCreate event), assign the event handlers and call register. And of course free the object instance in the destructor:
constructor TDragDropMainForm.Create(AOwner: TComponent); begin inherited Create(AOwner); FDropTarget := TDropTarget.Create; FDropTarget.OnDropAllowed := ImageDropAllowedHandler; FDropTarget.OnDrop := ImageDropHandler; FDropTarget.OnDragOver := ImageDragOverHandler; FDropTarget.Register(Handle); end; destructor TDragDropMainForm.Destroy; begin FreeandNil(FDropTarget); inherited; end;The demo application uses a TListView in vsList view mode and a TImage. The ListView accept the dropped images from Windows Explorer while TImage accepte images dropped from TListView. It is a good exercise for you to make TImage accept image also from Windows Explorer.
The demo application doesn’t show image in real size in TListView. Rather, it creates a thumbnail which is displayed in the list view. The thumbnails are stored on disk in the same folder as the original image and are only created if it doesn’t exist yet, or if the original image has been modified. Storing the thumbnail on disk could be a problem in some application because it requires write permission. In my application (Well the application I extracted this code from), it is an advantage because the images are very large and it takes time to create the thumbnails. Keeping the thumbnails on disk improve performance.
Thumbnails are created using GDI+ (See my other blog article about it: http://francois-piette.blogspot.be/2013/05/opensource-gdi-library.html). The code is really easy:
Image := TGPImage.Create(AFileName); Thumbnail := Image.GetThumbnailImage(ThWidth, ThHeight, nil, nil); Quality := 50; Params := TGPEncoderParameters.Create; Params.Add(EncoderQuality, Quality); Thumbnail.Save(AThumbFileName, TGPImageFormat.Jpeg, Params);
A last note about the demo application: I used custom draw of the ListView items so that it looks exactly how I require it. All list view items are represented by a class named TImageListViewItem. I have selected this representation because in the real application this demo is extracted from, there is a lot of information about each image. The class is really handy to hold the information and the processing related to it.
Here after is the complete source code. There are mainly two files: DropHanlder.pas and DragDropMain.pas. You can also download a zip file with the complete project. See my website at: http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html
DropHandler.pas
unit DropHandler; interface uses Windows, Types, Classes, SysUtils, ShellAPI, ActiveX; type TStringArray = array of String; TDropAllowedEvent = procedure (Sender : TObject; const FileNames : array of String; const grfKeyState : Longint; const pt : TPoint; var Allowed : Boolean) of object; TDragOverEvent = procedure (Sender : TObject; const grfKeyState : Longint; const pt : TPoint; var Allowed : Boolean) of object; TDropEvent = procedure (Sender : TObject; const DropPoint : TPoint; const FileNames : array of String) of object; TDropTarget = class(TObject, IDropTarget) private FRegisteredHandle : HWND; FDropAllowed : Boolean; FOnDropAllowed : TDropAllowedEvent; FOnDrop : TDropEvent; FOnDragOver : TDragOverEvent; FOnDragLeave : TNotifyEvent; procedure GetFileNames(const dataObj : IDataObject; var FileNames : TStringArray); function DragEnter(const dataObj : IDataObject; grfKeyState : Integer; pt : TPoint; var dwEffect : Integer): HResult; stdcall; function DragOver(grfKeyState : Longint; pt : TPoint; var dwEffect : Longint): HResult; stdcall; function DragLeave: HResult; stdcall; function Drop(const dataObj : IDataObject; grfKeyState : Longint; pt : TPoint; var dwEffect : Longint): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; public destructor Destroy; override; // Call Register() with a window handle so that that window starts // accepting dropped files. Events will then be generated. function Register(AHandle : HWnd) : HResult; // Stop accepting files dropped on the registered window. procedure Revoke; // DropAllowed event is called once when the dragged files are // entering the area of the registered window. // The event handler must set the Allowed var argument to TRUE if // dropping the file(s) is allowed at the given point property OnDropAllowed : TDropAllowedEvent read FOnDropAllowed write FOnDropAllowed; // DragOver event is called as mouse move above the registered window // The event handler must set the Allowed var argument to TRUE if // dropping the file(s) is allowed at the given point property OnDragOver : TDragOverEvent read FOnDragOver write FOnDragOver; // Drop event is called when the user drops the files. property OnDrop : TDropEvent read FOnDrop write FOnDrop; // DragLeave event is called when the dragged files leave the // registered window area. property OnDragLeave : TNotifyEvent read FOnDragLeave write FOnDragLeave; end; implementation {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget.Register(AHandle: HWnd): HResult; begin if FRegisteredHandle = AHandle then begin Result := S_OK; Exit; end; if FRegisteredHandle <> 0 then Revoke; FRegisteredHandle := AHandle; Result := ActiveX.RegisterDragDrop(FRegisteredHandle, Self); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDropTarget.Revoke; begin if FRegisteredHandle <> 0 then begin ActiveX.RevokeDragDrop(FRegisteredHandle); FRegisteredHandle := 0; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} destructor TDropTarget.Destroy; begin Revoke; inherited Destroy; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDropTarget.GetFileNames( const dataObj : IDataObject; var FileNames : TStringArray); var I : Integer; FormatetcIn : TFormatEtc; Medium : TStgMedium; DropHandle : HDROP; begin FileNames := nil; FormatetcIn.cfFormat := CF_HDROP; FormatetcIn.ptd := nil; FormatetcIn.dwAspect := DVASPECT_CONTENT; FormatetcIn.lindex := -1; FormatetcIn.tymed := TYMED_HGLOBAL; if dataObj.GetData(FormatetcIn, Medium) = S_OK then begin DropHandle := HDROP(Medium.hGlobal); SetLength(FileNames, DragQueryFile(DropHandle, $FFFFFFFF, nil, 0)); for I := 0 to high(FileNames) do begin SetLength(FileNames[I], DragQueryFile(DropHandle, I, nil, 0)); DragQueryFile(DropHandle, I, @FileNames[I][1], Length(FileNames[I]) + 1); end; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget.DragEnter( const dataObj : IDataObject; grfKeyState : Integer; pt : TPoint; var dwEffect : Integer): HResult; var FileNames: TStringArray; begin Result := S_OK; try GetFileNames(dataObj, FileNames); if (Length(FileNames) > 0) and Assigned(FOnDropAllowed) then begin FDropAllowed := FALSE; FOnDropAllowed(Self, FileNames, grfKeyState, pt, FDropAllowed); end; if FDropAllowed then dwEffect := DROPEFFECT_COPY else dwEffect := DROPEFFECT_NONE; except Result := E_UNEXPECTED; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget.DragLeave: HResult; begin if Assigned(FOnDragLeave) then FOnDragLeave(Self); Result := S_OK; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget.DragOver( grfKeyState : Integer; pt : TPoint; var dwEffect : Integer): HResult; begin Result := S_OK; try if Assigned(FOnDragOver) then FOnDragOver(Self, grfKeyState, pt, FDropAllowed); if FDropAllowed then dwEffect := DROPEFFECT_COPY else dwEffect := DROPEFFECT_NONE; except Result := E_UNEXPECTED; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget.Drop( const dataObj : IDataObject; grfKeyState : Integer; pt : TPoint; var dwEffect : Integer): HResult; var FileNames: TStringArray; begin Result := S_OK; try GetFileNames(dataObj, FileNames); if (Length(FileNames) > 0) and Assigned(FOnDrop) then FOnDrop(Self, Pt, FileNames); except // Silently ignore any exception bacsue if required, they should // be handled in OnDrop event handler. end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget._AddRef: Integer; begin // We don't use reference counting in this object // We need _AddRef because RegisterDragDrop API call it Result := 1; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDropTarget._Release: Integer; begin // We don't use reference counting in this object // We need _Release because RevokeDragDrop API call it Result := 1; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} end.
DragDropMain.pas
unit DragDropMain; interface uses Windows, Messages, Types, SysUtils, Variants, Classes, Graphics, StdCtrls, ExtCtrls, Controls, ComCtrls, CommCtrl, Forms, Dialogs, Jpeg, ImgList, GdiPlus, DropHandler; const AtEndOfPipe = -1; AtTopOfPipe = -2; THUMBNAIL_SIZE = 64; THUMBNAIL_MARGIN = 8; // List of accepted image file extensions Exts : array [0..3] of String = ('.jpg', '.png', '.bmp', '.tif'); type TImageListViewItem = class public FileName : String; Bitmap : TBitmap; Data : TObject; ThumbnailFileName : String; constructor Create(const AFileName : String; const AThumbnailFileName : String; const AItem : TListItem; const AWidth : Integer; const AHeight : Integer); destructor Destroy; override; end; TDragDropMainForm = class(TForm) ListView1: TListView; Splitter1: TSplitter; Image1: TImage; procedure ListView1CustomDrawItem(Sender : TCustomListView; Item : TListItem; State : TCustomDrawState; var DefaultDraw : Boolean); procedure ListView1Deletion(Sender : TObject; Item : TListItem); procedure ListView1MouseDown(Sender : TObject; Button : TMouseButton; Shift : TShiftState; X, Y : Integer); procedure ListView1MouseMove(Sender: TObject; Shift : TShiftState; X, Y : Integer); procedure ListView1MouseUp(Sender : TObject; Button : TMouseButton; Shift : TShiftState; X, Y : Integer); private FDropTarget : TDropTarget; FMouseDownPt : TPoint; FMouseMovePt : TPoint; FMouseDownFlag : Boolean; FDraggingImage : Boolean; procedure ImageDragOverHandler(Sender : TObject; const grfKeyState : Longint; const pt : TPoint; var Allowed : Boolean); procedure ImageDropAllowedHandler(Sender : TObject; const FileNames : array of string; const GrfKeyState : Integer; const Pt : TPoint; var Allowed : Boolean); procedure ImageDropHandler(Sender : TObject; const DropPoint : TPoint; const FileNames : array of string); function DropImage(const AFileName : String; XScreen : Integer; YScreen : Integer): Boolean; procedure CreateThumbnail(const AFileName : String; var AThumbFileName : String); function KnownExtension(const FileName : String): Boolean; overload; function KnownExtension(const FileNames: array of string): Boolean; overload; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; procedure AddImage(const FileName : String; BeforeIndex : Integer); procedure MoveImage(IFrom, ITo: Integer); procedure RemoveImage(Index: Integer); overload; function FindImage(const FileName: String): Integer; function AppendImage(const FileName: String): Integer; end; function ReplaceThumb(const FileName : String) : String; function ListViewMouseToItem( Pt : TPoint; LV : TListView; var ColIndex : Integer): TListItem; var DragDropMainForm: TDragDropMainForm; implementation {$R *.dfm} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} { TDragDropMainForm } {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} constructor TDragDropMainForm.Create(AOwner: TComponent); begin inherited Create(AOwner); FDropTarget := TDropTarget.Create; FDropTarget.OnDropAllowed := ImageDropAllowedHandler; FDropTarget.OnDrop := ImageDropHandler; FDropTarget.OnDragOver := ImageDragOverHandler; FDropTarget.Register(Handle); // To have TListView work correctly in vsList view mode, we must have // at least one group, one column and a SmallImages image list. ListView1.Groups.Clear; ListView1.Groups.Add; ListView1.Columns.Clear; ListView1.Columns.Add; // Height of displayed image is set by height of SmallImages ListView1.SmallImages := TImageList.Create(Self); ListView1.SmallImages.Height := THUMBNAIL_SIZE + 2 * THUMBNAIL_MARGIN; // Width of displayed image is set by ListView_SetColumnWidth macro with // column index set to zero. ListView_SetColumnWidth(ListView1.Handle, 0, THUMBNAIL_SIZE + 2 * THUMBNAIL_MARGIN); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} destructor TDragDropMainForm.Destroy; begin FreeandNil(FDropTarget); inherited; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ImageDragOverHandler( Sender : TObject; const grfKeyState : Longint; const pt : TPoint; var Allowed : Boolean); begin Allowed := TRUE; if not PtInRect(ListView1.BoundsRect, ListView1.ScreenToClient(Pt)) then begin Allowed := FALSE; Exit; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ImageDropAllowedHandler( Sender : TObject; const FileNames : array of string; const GrfKeyState : Integer; const Pt : TPoint; var Allowed : Boolean); begin Allowed := KnownExtension(FileNames); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ImageDropHandler( Sender : TObject; const DropPoint : TPoint; const FileNames : array of string); var I : Integer; begin for I := 0 to High(FileNames) do DropImage(ReplaceThumb(FileNames[I]), DropPoint.X, DropPoint.Y); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDragDropMainForm.DropImage( const AFileName : String; XScreen : Integer; YScreen : Integer) : Boolean; var Pt : TPoint; Item : TListItem; ColIndex : Integer; begin Result := FALSE; // First check if the extension is allowed if not KnownExtension(AFileName) then begin ShowMessage('Unacceptable file type (' + ExtractFileExt(AFileName) + ')'); Exit; end; // Check if we already got the image if FindImage(AFileName) >= 0 then begin ShowMessage(AFileName + #10 + 'Already in the ListView, ignoring'); Exit; end; // Check if the drop point is inside the ListView Pt := ListView1.ScreenToClient(Point(XScreen, YScreen)); if not PtInRect(ListView1.BoundsRect, Pt) then Exit; // Check if dropped on an existing item Item := ListViewMouseToItem(Pt, ListView1, ColIndex); if not Assigned(Item) then AppendImage(AFileName) // Not on an item, add at the end else AddImage(AFileName, Item.Index); // Insert before the item Result := TRUE; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ListView1CustomDrawItem( Sender : TCustomListView; Item : TListItem; State : TCustomDrawState; var DefaultDraw : Boolean); var Bitmap : TBitMap; Rc1 : TRect; Rc2 : TRect; Rc3 : TRect; ACanvas : TCanvas; YOff : Integer; XOff : Integer; begin ACanvas := Sender.Canvas; Rc1 := Item.DisplayRect(drBounds); if Assigned(Item.Data) then begin Bitmap := TImageListViewItem(Item.Data).Bitmap; // Center the bitmap YOff := ((THUMBNAIL_SIZE - BitMap.Height) div 2) + THUMBNAIL_MARGIN; XOff := ((THUMBNAIL_SIZE - Bitmap.Width) div 2) + THUMBNAIL_MARGIN; ACanvas.Draw(Rc1.Left + 2 + XOff, Rc1.Top + 2 + YOff, Bitmap); // Draw a double FrameRect around the image with a color depending // on the status of the image Rc2.Left := Rc1.Left + XOff; Rc2.Top := Rc1.Top + YOff; Rc2.Right := Rc1.Left + Bitmap.Width + 4 + XOff; Rc2.Bottom := Rc1.Top + Bitmap.Height + 4 + YOff; Rc3.Left := Rc1.Left + 1 + XOff; Rc3.Top := Rc1.Top + 1 + YOff; Rc3.Right := Rc1.Left + Bitmap.Width + 3 + XOff; Rc3.Bottom := Rc1.Top + Bitmap.Height + 3 + YOff; if cdsSelected in State then ACanvas.Brush.Color := clBlue else if cdsHot in State then ACanvas.Brush.Color := clRed else ACanvas.Brush.Color := ListView1.Color; ACanvas.FrameRect(Rc2); ACanvas.FrameRect(Rc3); DefaultDraw := FALSE; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ListView1Deletion( Sender : TObject; Item : TListItem); begin if Assigned(Item.Data) then TObject(Item.Data).Free; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ListView1MouseDown( Sender : TObject; Button : TMouseButton; Shift : TShiftState; X, Y : Integer); begin if ssLeft in Shift then begin FMouseDownPt := Point(X, Y); FMouseDownFlag := TRUE; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ListView1MouseMove( Sender : TObject; Shift : TShiftState; X, Y : Integer); var Item : TListItem; ColIndex : Integer; begin FMouseMovePt := Point(X, Y); if not FMouseDownFlag then Exit; if not FDraggingImage then begin Item := ListViewMouseToItem(FMouseDownPt, ListView1, ColIndex); if Assigned(Item) then begin FDraggingImage := TRUE; Screen.Cursor := crDrag; SetCaptureControl(ListView1); end; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.ListView1MouseUp( Sender : TObject; Button : TMouseButton; Shift : TShiftState; X, Y : Integer); var Pt : TPoint; ItemFrom : TListItem; ItemTo : TListItem; ColIndex : Integer; LV : TListView; IFrom : Integer; ITo : Integer; FileName : String; begin FMouseDownFlag := FALSE; if FDraggingImage then begin FDraggingImage := FALSE; Screen.Cursor := crDefault; SetCaptureControl(nil); LV := Sender as TListView; ItemFrom := ListViewMouseToItem(FMouseDownPt, LV, ColIndex); ItemTo := ListViewMouseToItem(Point(X, Y), LV, ColIndex); IFrom := ItemFrom.Index; FileName := TImageListViewItem(ItemFrom.Data).FileName; if not FileExists(FileName) then begin if Application.MessageBox( PChar('File "' + FileName + '" doesn''t exist anymore' + #10 + 'Remove from ListView ?'), 'WARNING', MB_YESNO + MB_DEFBUTTON2) = IDYES then begin RemoveImage(IFrom); Exit; end; end; if Assigned(ItemTo) then begin // Drop inside of the pipe, move items around if ItemTo <> ItemFrom then begin ITo := ItemTo.Index; MoveImage(IFrom, ITo); end; end else begin if PtInRect(LV.BoundsRect, Point(X, Y)) then begin // Drop on the listview but not on an item, just move at the end ITo := LV.Items.Count - 1; MoveImage(IFrom, ITo); end else begin // Drop outside of the ListView // Check if within Image1 Pt := ListView1.ClientToScreen(Point(X, Y)); Pt := Image1.ScreenToClient(Pt); if (Pt.X >= 0) and (Pt.X < Image1.Width) and (Pt.Y >= 0) and (Pt.Y < Image1.Height) then begin Image1.Picture.LoadFromFile(TImageListViewItem(ItemFrom.Data).FileName); end; end; end; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.AddImage( const FileName : String; BeforeIndex : Integer); // Index of the item where to insert (before) var IFrom : Integer; begin IFrom := AppendImage(FileName); if IFrom < 0 then Exit; // Not found or already exist, not added if BeforeIndex = AtTopOfPipe then MoveImage(IFrom, 0) else if (BeforeIndex >= 0) and (BeforeIndex < ListView1.Items.Count) then MoveImage(IFrom, BeforeIndex); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} // Search if a give file already exists in list. // Return -1 if not found // Return item index if already in the list function TDragDropMainForm.FindImage(const FileName: String): Integer; begin for Result := 0 to ListView1.Items.Count - 1 do begin if SameText(FileName, TImageListViewItem(ListView1.Items[Result].Data).FileName) then Exit; end; Result := -1; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.MoveImage( IFrom : Integer; ITo : Integer); var Data : Pointer; Capt : String; I : Integer; begin if IFrom < ITo then begin Data := ListView1.Items[IFrom].Data; Capt := ListView1.Items[IFrom].Caption; for I := IFrom to ITo - 1 do begin ListView1.Items[I].Data := ListView1.Items[I + 1].Data; ListView1.Items[I].Caption := ListView1.Items[I + 1].Caption; TImageListViewItem(ListView1.Items[I].Data).Data := ListView1.Items[I]; end; ListView1.Items[ITo].Data := Data; ListView1.Items[ITo].Caption := Capt; TImageListViewItem(ListView1.Items[ITo].Data).Data := ListView1.Items[ITo]; end else begin Data := ListView1.Items[IFrom].Data; Capt := ListView1.Items[IFrom].Caption; for I := IFrom downto ITo + 1 do begin ListView1.Items[I].Data := ListView1.Items[I - 1].Data; ListView1.Items[I].Caption := ListView1.Items[I - 1].Caption; TImageListViewItem(ListView1.Items[I].Data).Data := ListView1.Items[I]; end; ListView1.Items[ITo].Data := Data; ListView1.Items[ITo].Caption := Caption; TImageListViewItem(ListView1.Items[ITo].Data).Data := ListView1.Items[ITo]; end; Windows.InvalidateRect(ListView1.Handle, nil, FALSE); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDragDropMainForm.KnownExtension( const FileName : String) : Boolean; var Ext : String; I : Integer; begin Result := FALSE; Ext := ExtractFileExt(FileName); for I := Low(Exts) to High(Exts) do begin if SameText(Ext, Exts[I]) then begin Result := TRUE; Exit; end; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDragDropMainForm.KnownExtension( const FileNames : array of string) : Boolean; var I : Integer; begin Result := FALSE; for I := Low(FileNames) to High(FileNames) do begin if KnownExtension(FileNames[I]) then begin Result := TRUE; Exit; end; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} // Given a filename which could be a thumbnail filename, return either the // filename unchanged or the image which is represented by thumbnail function ReplaceThumb(const FileName : String) : String; const ThSuffix = '.thumb.jpg'; var S : String; I : Integer; begin if not SameText(Copy(FileName, Length(FileName) - Length(ThSuffix) + 1, 200), ThSuffix) then begin Result := FileName; Exit; end; S := Copy(FileName, 1, Length(FileName) - Length(ThSuffix)); for I := Low(Exts) to High(Exts) do begin Result := S + Exts[I]; if FileExists(Result) then Exit; end; Result := FileName; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} // ColIndex returns the column index, not the SubItem index. function ListViewMouseToItem( Pt : TPoint; LV : TListView; var ColIndex : Integer): TListItem; var Info : TLVHitTestInfo; begin // Pt := LV.ScreenToClient(Mouse.Cursorpos); Result := LV.GetItemAt(Pt.X, Pt.Y); if Assigned(Result) then ColIndex := 0 else begin FillChar(Info, SizeOf(Info), 0); Info.Pt := Pt; if LV.Perform(LVM_SUBITEMHITTEST, 0, LParam(@Info)) <> -1 then begin Result := LV.Items[Info.iItem]; ColIndex := Info.iSubItem; end; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function TDragDropMainForm.AppendImage( const FileName : String) : Integer; var Item : TListItem; ThumbnailFileName : String; begin Result := -1; if not FileExists(FileName) then Exit; if FindImage(FileName) >= 0 then Exit; // Already exist, do not add ThumbnailFileName := ''; CreateThumbnail(FileName, ThumbnailFileName); Item := ListView1.Items.Add; // Item.Caption is used as the hint Item.Caption := FileName; Item.Data := TImageListViewItem.Create(FileName, ThumbnailFileName, Item, THUMBNAIL_SIZE, THUMBNAIL_SIZE); Result := Item.Index; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.RemoveImage(Index : Integer); var I : Integer; begin ListView1.Items.Delete(Index); for I := Index to ListView1.Items.Count - 1 do TImageListViewItem(ListView1.Items[I].Data).Data := ListView1.Items[I]; Windows.InvalidateRect(ListView1.Handle, nil, FALSE); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TDragDropMainForm.CreateThumbnail( const AFileName : String; var AThumbFileName : String); var ThWidth : Integer; ThHeight : Integer; FTFile : TDateTime; FTThumb : TDateTime; Image : IGPImage; Thumbnail : IGPImage; Params : IGPEncoderParameters; Quality : Int32; begin AThumbFileName := ChangeFileExt(AFileName, '.thumb.jpg'); if FileExists(AThumbFileName) then begin // Thumbnail file must be dated AFTER original file so that it // is recreated when the original file is changed. FileAge(AFileName, FTFile); FileAge(AThumbFileName, FTThumb); if FTThumb >= FTFile then Exit; end; Image := TGPImage.Create(AFileName); // Thumbnail preserve original width/height ratio if Image.Width > Image.Height then begin ThWidth := THUMBNAIL_SIZE; ThHeight := THUMBNAIL_SIZE * Image.Height div Image.Width; end else if Image.Width < Image.Height then begin ThHeight := THUMBNAIL_SIZE; ThWidth := THUMBNAIL_SIZE * Image.Width div Image.Height; end else begin ThWidth := THUMBNAIL_SIZE; ThHeight := THUMBNAIL_SIZE; end; Thumbnail := Image.GetThumbnailImage(ThWidth, ThHeight, nil, nil); Quality := 50; Params := TGPEncoderParameters.Create; Params.Add(EncoderQuality, Quality); Thumbnail.Save(AThumbFileName, TGPImageFormat.Jpeg, Params); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} { TImagePipeItem } {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} constructor TImageListViewItem.Create( const AFileName : String; const AThumbnailFileName : String; const AItem : TListItem; const AWidth : Integer; const AHeight : Integer); var JpegImg : TJPEGImage; Ext : String; begin inherited Create; Data := AItem; FileName := AFileName; ThumbnailFileName := AThumbnailFileName; Bitmap := TBitMap.Create; if (AThumbnailFileName <> '') and (FileExists(AThumbnailFileName)) then begin Ext := ExtractFileExt(AThumbnailFileName); if SameText(Ext, '.jpg') then begin JpegImg := TJPEGImage.Create; try JpegImg.LoadFromFile(AThumbnailFileName); BitMap.Width := JpegImg.Width; BitMap.Height := JpegImg.Height; BitMap.Canvas.Draw(0, 0, JpegImg); finally JpegImg.Destroy; end; end else if SameText(Ext, '.bmp') then Bitmap.LoadFromFile(AThumbnailFileName) end else begin Bitmap.Width := AWidth - 4; Bitmap.Height := AHeight - 4; Bitmap.PixelFormat := pf24bit; Bitmap.Canvas.MoveTo(0, 0); BitMap.Canvas.LineTo(Bitmap.Width, Bitmap.Height); Bitmap.Canvas.MoveTo(Bitmap.Width, 0); BitMap.Canvas.LineTo(0, Bitmap.Height); BitMap.Canvas.LineTo(0, 0); BitMap.Canvas.LineTo(Bitmap.Width - 1, 0); BitMap.Canvas.LineTo(Bitmap.Width - 1, Bitmap.Height - 1); BitMap.Canvas.LineTo(0, Bitmap.Height - 1); Bitmap.Canvas.TextOut(4, 4, AFileName); end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} destructor TImageListViewItem.Destroy; begin FreeAndNil(Bitmap); inherited Destroy; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} end.
Download source code from: http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
dimanche 24 février 2013
Test Number 2
Author: François PIETTE @ OverByte
Creation: Feb 16, 2013
Description: TCommQueue is a generic class aimed at handling "buffers" in
an efficient way so that it can be used in high speed systems.
The idea is to have the minimum number of memory allocation.
To manage the buffers, the class uses 3 linked lists: Active,
inactive and acquired buffers.
When adding a buffer to the queue, the Add method first look at
the inactive queue to get an inactive buffer and reuse it. If
no inactive buffer is available, then a new one is allocated.
When a buffer is not more needed, the remove method actually move
the buffer to the inactive buffer list.
The queue offers methods to iterate thru it: first, next, previous
and last. Each method return a pointer to the buffer.
The class is multithread safe: it make use of a critical section
to protect his internal data structures.
To be completely thread safe according to the producer/consumer
pattern, there are a pair of additional methods: AcquireItem and
ReleaseItem. AcquireItem will get a pointer to the oldest buffer
in the queue and move it to the acquired buffer list. Only a
single thread is able to get a pointer to a given buffer.
Once a thread has finished processing the buffer, it must call
the ReleaseItem method passing the pointer so that the buffer is
moved to the inactive list for later reuse.
A last method, FreeAllInactiveItems, can be use to free all
inactive buffer. This should probably never used unless the
application is short of memory. Freeing all inactive buffer
obviously frre memory, but has an impact on performance: new
buffer must be allocated.
On my system, allocating one million of buffers having 0.5 kB each
takes 265 mS the first time. It takes 109 mS to remove all.
Then it only takes 145 mS to get another one million buffers.
It goes faster the second (and subsequent times) because no
memory allocation take place. Only pointers in the linked lists
are updated.
Note about multithreading support: All operations on the linked
lists are protected by a critical section so each list will
remain coherent even if different threads use the same queue at
the same time. However, if several thread have to process items
in the queue (consumers), they MUST use AcquireItem and
ReleaseItem exclusively. Other methods are for a single consumer
thread. In all cases, multiple thread can be producers, that is
add items in the queue.
Version: 1.00
History:
mercredi 20 février 2013
Test number 1
This is a test.
function TMyObjct.HelloSyntaxHighlighter(const S : String); begin WriteLn(S); if S < '' then WriteLn('Test'); end;Please ignore.
Inscription à :
Articles (Atom)