dropfile.pas

来自「Drag files and Drop to delphi forms 0402」· PAS 代码 · 共 707 行 · 第 1/2 页

PAS
707
字号

//--------------------------
// SOURCE events...
//--------------------------
procedure TFormFile.DropSource1Feedback(Sender: TObject; Effect: Integer;
  var UseDefaultCursors: Boolean);
begin
  UseDefaultCursors := False; // We want to use our own.
  case DWORD(Effect) of
    DROPEFFECT_COPY:
      Windows.SetCursor(Screen.Cursors[crCopy]);
    DROPEFFECT_MOVE:
      Windows.SetCursor(Screen.Cursors[crMove]);
    DROPEFFECT_LINK:
      Windows.SetCursor(Screen.Cursors[crLink]);
    DROPEFFECT_SCROLL OR DROPEFFECT_COPY:
      Windows.SetCursor(Screen.Cursors[crCopyScroll]);
    DROPEFFECT_SCROLL OR DROPEFFECT_MOVE:
      Windows.SetCursor(Screen.Cursors[crMoveScroll]);
    DROPEFFECT_SCROLL OR DROPEFFECT_LINK:
      Windows.SetCursor(Screen.Cursors[crLinkScroll]);
  else
    UseDefaultCursors := True; // Use default NoDrop
  end;
end;

procedure TFormFile.DropSource1AfterDrop(Sender: TObject;
  DragResult: TDragResult; Optimized: Boolean);
var
  i: integer;
begin
  // Delete source files if target performed an unoptimized drag/move
  // operation (target copies files, source deletes them).
  if (DragResult = drDropMove) and (not Optimized) then
    for i := 0 to DropSource1.Files.Count-1 do
      DeleteFile(DropSource1.Files[i]);
end;

procedure TFormFile.DropSource1Paste(Sender: TObject; Action: TDragResult;
  DeleteOnPaste: Boolean);
var
  i: integer;
begin
  StatusBar1.SimpleText := 'Target pasted file(s)';

  // Delete source files if target performed a paste/move operation and
  // requested the source to "Delete on paste".
  if (DeleteOnPaste) then
    for i := 0 to DropSource1.Files.Count-1 do
      DeleteFile(DropSource1.Files[i]);
end;


//--------------------------
// TARGET events...
//--------------------------
procedure TFormFile.DropFileTarget1Enter(Sender: TObject;
  ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
begin
  // Note: GetDataOnEnter has been set to True.
  // A side effect of this is that TDropFileTarget can't be used to accept drops
  // from WinZip. Although the file names are received correctly, the files
  // aren't extracted yet and thus can't be copied/moved.
  // This is caused by a quirk in WinZip; Apparently WinZip doesn't like
  // IDataObject.GetData to be called before IDropTarget.Drop is called.

  // Save the location (path) of the files being dragged.
  // Also flags if an EXE file is being dragged.
  // This info will be used to set the default (ie. no Shift or Ctrl Keys
  // pressed) drag behaviour (COPY, MOVE or LINK).
  if (DropFileTarget1.Files.count > 0) then
  begin
    SourcePath := ExtractFilePath(DropFileTarget1.Files[0]);
    IsEXEfile := (DropFileTarget1.Files.count = 1) and
      (AnsiCompareText(ExtractFileExt(DropFileTarget1.Files[0]), '.exe') = 0);
  end;
end;

procedure TFormFile.DropFileTarget1Drop(Sender: TObject;
  ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
var
  i, SuccessCnt: integer;
  NewFilename: string;
  newPath: string;
begin
  SuccessCnt := 0;
  NewPath := AddSlash(DirectoryOutline.Directory);

  // Filter out the DROPEFFECT_SCROLL flag if set...
  // (ie: when dropping a file while the target window is scrolling)
  Effect := Effect and not DROPEFFECT_SCROLL;
  // Now, 'Effect' should equal one of the following:
  // DROPEFFECT_COPY, DROPEFFECT_MOVE or DROPEFFECT_LINK.
  // Note however, that if we call TDropTarget.PasteFromClipboard, Effect
  // can be a combination of the above drop effects.

  for i := 0 to DropFileTarget1.Files.count-1 do
  begin
    // Name mapping occurs when dragging files from Recycle Bin...
    // In most situations Name Mapping can be ignored entirely.
    if (i < DropFileTarget1.MappedNames.Count) then
      NewFilename := NewPath+DropFileTarget1.MappedNames[i]
    else
      NewFilename := NewPath+ExtractFilename(DropFileTarget1.Files[i]);

    if not FileExists(NewFilename) then
    begin
      if NewFilename = DropFileTarget1.Files[i] then
      begin
        Windows.MessageBox(Handle,
          'The destination folder is the same as the source!',
          'Drag/Drop Demo', mb_iconStop or mb_OK);
        Break;
      end;

      try
        if (Effect and DROPEFFECT_COPY <> 0) then
        begin
          Effect := DROPEFFECT_COPY;
          // Copy the file.
          if CopyFile(PChar(DropFileTarget1.Files[i]), PChar(NewFilename), True) then
            inc(SuccessCnt);
        end else
        if (Effect and DROPEFFECT_MOVE <> 0) then
        begin
          Effect := DROPEFFECT_MOVE;
          // Move the file.
          if RenameFile(DropFileTarget1.Files[i], NewFilename) then
            inc(SuccessCnt)
        end;
      except
        // Ignore errors.
      end;
    end;

    if (Effect and DROPEFFECT_LINK <> 0) then
    begin
      Effect := DROPEFFECT_LINK;
      // Create a shell link to the file.
      CreateLink(DropFileTarget1.Files[i], NewFilename);
      inc(SuccessCnt);
    end;

  end;

  if (Effect = DROPEFFECT_MOVE) then
    StatusBar1.SimpleText :=
      Format('%d file(s) were moved.   Files dropped at point (%d,%d).',
        [SuccessCnt, Point.x, Point.y])
  else if (Effect = DROPEFFECT_COPY) then
    StatusBar1.SimpleText :=
      Format('%d file(s) were copied.   Files dropped at point (%d,%d).',
        [SuccessCnt, Point.x, Point.y])
  else
    StatusBar1.SimpleText :=
      Format('%d file(s) were linked.   Files dropped at point (%d,%d).',
        [SuccessCnt, Point.x, Point.y]);
end;

procedure TFormFile.DropFileTarget1GetDropEffect(Sender: TObject;
  ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
var
  Scroll: DWORD;
begin
  // Note: The 'Effect' parameter (on event entry) is the
  // set of effects allowed by both the source and target.
  // Use this event when you wish to override the Default behaviour...

  // Save the value of the auto scroll flag.
  // As an alternative we could implement our own auto scroll logic here.
  Scroll := DWORD(Effect) and DROPEFFECT_SCROLL;

  // We're only interested in ssShift & ssCtrl here so
  // mouse buttons states are screened out ...
  ShiftState := ([ssShift, ssCtrl] * ShiftState);

  // Reject the drop if source and target paths are the same (DROPEFFECT_NONE).
  if (AddSlash(DirectoryOutline.Directory) = SourcePath) then
    Effect := DROPEFFECT_NONE
  // else if Ctrl+Shift are pressed then create a link (DROPEFFECT_LINK).
  else if (ShiftState = [ssShift, ssCtrl]) and
    (Effect and DROPEFFECT_LINK <> 0) then Effect := DROPEFFECT_LINK
  // else if Shift is pressed then move (DROPEFFECT_MOVE).
  else if (ShiftState = [ssShift]) and
    (Effect and DROPEFFECT_MOVE<>0) then Effect := DROPEFFECT_MOVE
  // else if Ctrl is pressed then copy (DROPEFFECT_COPY).
  else if (ShiftState = [ssCtrl]) and
    (Effect and DROPEFFECT_COPY<>0) then Effect := DROPEFFECT_COPY
  // else if dragging a single EXE file then default to link (DROPEFFECT_LINK).
  else if IsEXEfile and (Effect and DROPEFFECT_LINK<>0) then
    Effect := DROPEFFECT_LINK
  // else if source and target drives are the same then default to MOVE (DROPEFFECT_MOVE).
  else if (SourcePath <> '') and (DirectoryOutline.Directory[1] = SourcePath[1]) and
    (Effect and DROPEFFECT_MOVE<>0) then Effect := DROPEFFECT_MOVE
  // otherwise just use whatever we can get away with.
  else if (Effect and DROPEFFECT_COPY<>0) then Effect := DROPEFFECT_COPY
  else if (Effect and DROPEFFECT_MOVE<>0) then Effect := DROPEFFECT_MOVE
  else if (Effect and DROPEFFECT_LINK<>0) then Effect := DROPEFFECT_LINK
  else Effect := DROPEFFECT_NONE;

  // Restore auto scroll flag.
  Effect := Effect or integer(Scroll);
end;

//----------------------------------------------------------------------------
// TDirectoryThread
// This thread monitors the current directory for changes and updates the
// listview whenever the directory is changed (files added, renamed or deleted).
//----------------------------------------------------------------------------

// OK, we're showing off... This is a little overkill for a demo,
// but still you can see what can be done.
constructor TDirectoryThread.Create(ListView: TListView; Dir: string);
begin
  inherited Create(True);

  FListView := ListView;
  Priority := tpLowest;
  FDirectory := Dir;
  FWakeupEvent := Windows.CreateEvent(nil, False, False, nil);
  FFiles := TStringList.Create;

  Resume;
end;

destructor TDirectoryThread.Destroy;
begin
  CloseHandle(FWakeupEvent);
  FFiles.Free;
  inherited Destroy;
end;

procedure TDirectoryThread.WakeUp;
begin
  SetEvent(FWakeupEvent);
end;

procedure TDirectoryThread.SetDirectory(Value: string);
begin
  if (Value = FDirectory) then
    exit;
  FDirectory := Value;
  WakeUp;
end;

procedure TDirectoryThread.ScanDirectory;
var
  sr: TSearchRec;
  res: integer;
begin
  FFiles.Clear;
  res := FindFirst(AddSlash(FDirectory)+'*.*', 0, sr);
  try
    while (res = 0) and (not Terminated) do
    begin
      if (sr.Name <> '.') and (sr.Name <> '..') then
        FFiles.Add(lowercase(sr.Name));
      res := FindNext(sr);
    end;
  finally
    FindClose(sr);
  end;
end;

procedure TDirectoryThread.UpdateListView;
var
  NewItem: TListItem;
  i: integer;
begin
  fListView.Items.BeginUpdate;
  try
    fListView.Items.clear;
    for i := 0 to FFiles.Count-1 do
    begin
      NewItem := fListView.Items.Add;
      NewItem.Caption := FFiles[i];
    end;
    if fListView.Items.Count > 0 then
      fListView.ItemFocused := fListView.Items[0];
  finally
    fListView.Items.EndUpdate;
  end;
  FFiles.Clear;
end;

procedure TDirectoryThread.Execute;
var
  fFileChangeHandle: THandle;
begin

  // OUTER LOOP - which will exit only when terminated ...
  // directory changes will be processed within this OUTER loop
  // (file changes will be processed within the INNER loop)
  while (not Terminated) do
  begin
    ScanDirectory;
    Synchronize(UpdateListView);

    //Monitor directory for file changes
    fFileChangeHandle :=
      FindFirstChangeNotification(PChar(fDirectory), False,
        FILE_NOTIFY_CHANGE_FILE_NAME);
    if (fFileChangeHandle = INVALID_HANDLE_VALUE) then
      //Can't monitor filename changes! Just wait for change of directory or terminate
      WaitForSingleObject(FWakeupEvent, INFINITE)
    else
      try
        //This function performs an INNER loop...
        ProcessFilenameChanges(fFileChangeHandle);
      finally
        FindCloseChangeNotification(fFileChangeHandle);
      end;
  end;
end;

procedure TDirectoryThread.ProcessFilenameChanges(fcHandle: THandle);
var
  WaitResult: DWORD;
  HandleArray: array[0..1] of THandle;
begin
  HandleArray[0] := FWakeupEvent;
  HandleArray[1] := fcHandle;
  // INNER LOOP -
  // which will exit only if terminated or the directory is changed
  // filename changes will be processed within this loop
  while (not Terminated) do
  begin
    //wait for either filename or directory change, or terminate...
    WaitResult := WaitForMultipleObjects(2, PWOHandleArray(@HandleArray), False,
      INFINITE);

    if (WaitResult = WAIT_OBJECT_0 + 1) then //filename has changed
    begin
      repeat //collect all immediate filename changes...
        FindNextChangeNotification(fcHandle);
      until Terminated or (WaitForSingleObject(fcHandle, 0) <> WAIT_OBJECT_0);
      if Terminated then
        Break;
      // OK, now update (before restarting inner loop)...
      ScanDirectory;
      Synchronize(UpdateListView);
    end else
    begin // Either directory changed or terminated ...
      //collect all (almost) immediate directory changes before exiting...
      while (not Terminated) and
        (WaitForSingleObject(FWakeupEvent, 100) = WAIT_OBJECT_0) do {nothing};
      break;
    end;
  end;
end;

end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?