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
;
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;
protected
procedure
CreateWnd; override;
procedure
DestroyWnd; override;
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}
constructor
TDragDropMainForm
.
Create(AOwner: TComponent);
begin
FDropTarget := TDropTarget
.
Create;
FDropTarget
.
OnDropAllowed := ImageDropAllowedHandler;
FDropTarget
.
OnDrop := ImageDropHandler;
FDropTarget
.
OnDragOver := ImageDragOverHandler;
inherited
Create(AOwner);
ListView1
.
Groups
.
Clear;
ListView1
.
Groups
.
Add;
ListView1
.
Columns
.
Clear;
ListView1
.
Columns
.
Add;
ListView1
.
SmallImages := TImageList
.
Create(Self);
ListView1
.
SmallImages
.
Height := THUMBNAIL_SIZE +
2
* THUMBNAIL_MARGIN;
ListView_SetColumnWidth(ListView1
.
Handle,
0
, THUMBNAIL_SIZE +
2
* THUMBNAIL_MARGIN);
end
;
procedure
TDragDropMainForm
.
CreateWnd;
begin
inherited
CreateWnd;
if
Assigned(FDropTarget)
then
FDropTarget
.
Register(Handle);
end
;
destructor
TDragDropMainForm
.
Destroy;
begin
FreeandNil(FDropTarget);
inherited
;
end
;
procedure
TDragDropMainForm
.
DestroyWnd;
begin
inherited
DestroyWnd;
if
Assigned(FDropTarget)
then
FDropTarget
.
Revoke;
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
;
if
not
KnownExtension(AFileName)
then
begin
ShowMessage(
'Unacceptable file type ('
+
ExtractFileExt(AFileName) +
')'
);
Exit;
end
;
if
FindImage(AFileName) >=
0
then
begin
ShowMessage(AFileName + #
10
+
'Already in the ListView, ignoring'
);
Exit;
end
;
Pt := ListView1
.
ScreenToClient(Point(XScreen, YScreen));
if
not
PtInRect(ListView1
.
BoundsRect, Pt)
then
Exit;
Item := ListViewMouseToItem(Pt, ListView1, ColIndex);
if
not
Assigned(Item)
then
AppendImage(AFileName)
else
AddImage(AFileName, Item
.
Index);
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;
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);
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
if
ItemTo <> ItemFrom
then
begin
ITo := ItemTo
.
Index;
MoveImage(IFrom, ITo);
end
;
end
else
begin
if
PtInRect(LV
.
BoundsRect, Point(X, Y))
then
begin
ITo := LV
.
Items
.
Count -
1
;
MoveImage(IFrom, ITo);
end
else
begin
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
);
var
IFrom :
Integer
;
begin
IFrom := AppendImage(FileName);
if
IFrom <
0
then
Exit;
if
BeforeIndex = AtTopOfPipe
then
MoveImage(IFrom,
0
)
else
if
(BeforeIndex >=
0
)
and
(BeforeIndex < ListView1
.
Items
.
Count)
then
MoveImage(IFrom, BeforeIndex);
end
;
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
;
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
;
function
ListViewMouseToItem(
Pt : TPoint;
LV : TListView;
var
ColIndex :
Integer
): TListItem;
var
Info : TLVHitTestInfo;
begin
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;
ThumbnailFileName :=
''
;
CreateThumbnail(FileName, ThumbnailFileName);
Item := ListView1
.
Items
.
Add;
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
FileAge(AFileName, FTFile);
FileAge(AThumbFileName, FTThumb);
if
FTThumb >= FTFile
then
Exit;
end
;
Image := TGPImage
.
Create(AFileName);
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
;
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
.