| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283 | unit CustomDriveView;interfaceuses  Classes, ComCtrls, CommCtrl, Windows, Controls, Forms, ShlObj, Messages,    Graphics,  DragDrop, CustomDirView, IEDriveInfo, DragDropFilesEx, PasTools;type  {Types uses by the function IterateSubTree:}  {TRecursiveScan: determines, wich nodes are scanned by the function IterateSubTree:  rsNoRecursive:       Scan startnode only.  rsRecursiveExisting: Scan all subnodes of the startnode but not new created subnodes.}  TRecursiveScan = (rsNoRecursive, rsRecursiveExisting);  {TScanStartnode: determines, wether the startnode should also be scanned:}  TScanStartNode = (coNoScanStartNode, coScanStartNode);  TCallBackFunc = function(var Node: TTreeNode; Data: Pointer): Boolean of object;type  TCustomDriveView = class(TCustomTreeView)  protected    FParentForm: TCustomForm;    FDragFileList: TStringList;    FDragDropFilesEx: TCustomizableDragDropFilesEx;    FDragImageList: TDragImageList;    FDragDrive: string;    FExeDrag: Boolean;    FDDLinkOnExeDrag: Boolean;    FDragNode: TTreeNode;    FDragStartTime: FILETIME;    FDragPos: TPoint;    FStartPos: TPoint;    FContextMenu: Boolean;    FCanChange: Boolean;    FUseSystemContextMenu: Boolean;    FDimmHiddenDirs: Boolean;    FShowHiddenDirs: Boolean;    FNaturalOrderNumericalSorting: Boolean;    FDarkMode: Boolean;    FImageList: TImageList;    FScrollOnDragOver: TTreeViewScrollOnDragOver;    FRecreatingHandle: Boolean;    FOnDDDragEnter: TDDOnDragEnter;    FOnDDDragLeave: TDDOnDragLeave;    FOnDDDragOver: TDDOnDragOver;    FOnDDDrop: TDDOnDrop;    FOnDDQueryContinueDrag: TDDOnQueryContinueDrag;    FOnDDChooseEffect: TDDOnChooseEffect;    FOnDDGiveFeedback: TDDOnGiveFeedback;    FOnDDDragDetect: TDDOnDragDetect;    FOnDDProcessDropped: TOnProcessDropped;    FOnDDError: TDDErrorEvent;    FOnDDExecuted: TDDExecutedEvent;    FOnDDFileOperation: TDDFileOperationEvent;    FOnDDFileOperationExecuted: TDDFileOperationExecutedEvent;    FOnDDCreateDragFileList: TDDOnCreateDragFileList;    FOnDDEnd: TNotifyEvent;    FOnDDCreateDataObject: TDDOnCreateDataObject;    FLastDDResult: TDragResult;    FOnBusy: TDirViewBusy;    function GetTargetPopupMenu: Boolean;    procedure SetTargetPopUpMenu(Value: Boolean);    procedure SetDimmHiddenDirs(Value: Boolean);    procedure SetShowHiddenDirs(Value: Boolean);    procedure SetNaturalOrderNumericalSorting(Value: Boolean);    procedure SetDarkMode(Value: Boolean);    function GetDirectory: string; virtual;    procedure SetDirectory(Value: string); virtual;    function GetCustomDirView: TCustomDirView; virtual; abstract;    procedure SetCustomDirView(Value: TCustomDirView); virtual; abstract;    procedure CreateWnd; override;    procedure Notification(AComponent: TComponent; Operation: TOperation); override;    function GetNodeFromHItem(Item: TTVItem): TTreeNode;    function IsCustomDrawn(Target: TCustomDrawTarget; Stage: TCustomDrawStage): Boolean; override;    function CustomDrawItem(Node: TTreeNode; State: TCustomDrawState;      Stage: TCustomDrawStage; var PaintImages: Boolean): Boolean; override;    procedure NeedImageLists;    procedure DoCompare(Sender: TObject; Node1, Node2: TTreeNode; Data: Integer; var Compare: Integer);    function DoCompareText(Text1, Text2: string): Integer;    procedure UpdateItemHeight;    procedure CNNotify(var Msg: TWMNotify); message CN_NOTIFY;    procedure CMColorChanged(var Msg: TMessage); message CM_COLORCHANGED;    procedure CMRecreateWnd(var Msg: TMessage); message CM_RECREATEWND;    procedure WMLButtonDown(var Msg: TWMLButtonDown); message WM_LBUTTONDOWN;    procedure WMLButtonUp(var Msg: TWMLButtonDown); message WM_LBUTTONUP;    procedure WMRButtonDown(var Msg: TWMRButtonDown); message WM_RBUTTONDOWN;    procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;    procedure WMContextMenu(var Msg: TWMContextMenu); message WM_CONTEXTMENU;    procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;    procedure CMDPIChanged(var Message: TMessage); message CM_DPICHANGED;    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;    procedure ChangeScale(M, D: Integer; isDpiChange: Boolean); override;    procedure Delete(Node: TTreeNode); override;    procedure KeyDown(var Key: Word; Shift: TShiftState); override;    procedure KeyPress(var Key: Char); override;    procedure KeyUp(var Key: Word; Shift: TShiftState); override;    procedure InternalOnDrawItem(Sender: TCustomTreeView; Node: TTreeNode;      State: TCustomDrawState; var DefaultDraw: Boolean);    procedure DDDragEnter(DataObj: IDataObject; KeyState: Longint;      Point: TPoint; var Effect: Longint; var Accept: Boolean);    procedure DDDragLeave(Dummy: Integer);    procedure DDDragOver(KeyState: Longint; Point: TPoint; var Effect: Longint; PreferredEffect: LongInt);    procedure DDDrop(DataObj: IDataObject; KeyState: Longint; Point: TPoint;      var Effect: Longint);    procedure DDQueryContinueDrag(EscapePressed: BOOL; KeyState: Longint;      var Result: HResult);    procedure DDDropHandlerSucceeded(Sender: TObject; KeyState: Longint;      Point: TPoint; Effect: Longint);    procedure DDGiveFeedback(Effect: Longint; var Result: HResult);    procedure DDProcessDropped(Sender: TObject; KeyState: Longint;      Point: TPoint; Effect: Longint);    procedure DDError(Error: TDDError); virtual;    procedure DDSpecifyDropTarget(Sender: TObject; DragDropHandler: Boolean;      Point: TPoint; var PIDL: PItemIDList; var Filename: string);    procedure DDDragDetect(KeyState: Longint; DetectStart, Point: TPoint;      DragStatus: TDragDetectStatus); virtual;    procedure PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer); virtual; abstract;    procedure DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer); virtual;    function DragCompleteFileList: Boolean; virtual; abstract;    function DDExecute: TDragResult; virtual;    function DDSourceEffects: TDropEffectSet; virtual; abstract;    function NodePath(Node: TTreeNode): string; virtual; abstract;    function NodeIsRecycleBin(Node: TTreeNode): Boolean; virtual;    function NodePathExists(Node: TTreeNode): Boolean; virtual;    function NodeColor(Node: TTreeNode): TColor; virtual; abstract;    function NodeCanDrag(Node: TTreeNode): Boolean; virtual;    function NodeOverlayIndexes(Node: TTreeNode): Word; virtual;    function FindPathNode(Path: string): TTreeNode; virtual; abstract;    procedure ClearDragFileList(FileList: TFileList); virtual;    procedure AddToDragFileList(FileList: TFileList; Node: TTreeNode); virtual;    procedure ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;      NewDirs: Boolean); virtual; abstract;    procedure RebuildTree; virtual; abstract;    procedure DisplayContextMenu(Node: TTreeNode; ScreenPos: TPoint); virtual; abstract;    procedure DisplayPropertiesMenu(Node: TTreeNode); virtual; abstract;    procedure ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);    procedure ScrollOnDragOverAfterUpdate;    function DoBusy(Busy: Integer): Boolean;    function StartBusy: Boolean;    procedure EndBusy;    function IsBusy: Boolean;    property ImageList: TImageList read FImageList;  public    constructor Create(AOwner: TComponent); override;    destructor Destroy; override;    procedure ValidateDirectory(Node: TTreeNode);    function SortChildren(ParentNode: TTreeNode; Recurse: Boolean): Boolean;    function IterateSubTree(var StartNode : TTreeNode;      CallBackFunc: TCallBackFunc; Recurse: TRecursiveScan;      ScanStartNode: TScanStartNode; Data: Pointer): Boolean;    function NodePathName(Node: TTreeNode): string; virtual; abstract;    property DragDropFilesEx: TCustomizableDragDropFilesEx read FDragDropFilesEx;    property UseSystemContextMenu: Boolean read FUseSystemContextMenu      write FUseSystemContextMenu default True;    property DimmHiddenDirs: Boolean read FDimmHiddenDirs      write SetDimmHiddenDirs default False;    property ShowHiddenDirs: Boolean read FShowHiddenDirs      write SetShowHiddenDirs default False;    property NaturalOrderNumericalSorting: Boolean read FNaturalOrderNumericalSorting write SetNaturalOrderNumericalSorting;    property DarkMode: Boolean read FDarkMode write SetDarkMode;    property DDLinkOnExeDrag: Boolean read FDDLinkOnExeDrag write FDDLinkOnExeDrag default True;    {The mouse has entered the component window as a target of a drag&drop operation:}    property OnDDDragEnter: TDDOnDragEnter read FOnDDDragEnter write FOnDDDragEnter;    {The mouse has leaved the component window as a target of a drag&drop operation:}    property OnDDDragLeave: TDDOnDragLeave read FOnDDDragLeave write FOnDDDragLeave;    {The mouse is dragging in the component window as a target of a drag&drop operation:}    property OnDDDragOver: TDDOnDragOver read FOnDDDragOver write FOnDDDragOver;    {The Drag&drop operation is about to be executed:}    property OnDDDrop: TDDOnDrop read FOnDDDrop write FOnDDDrop;    property OnDDQueryContinueDrag: TDDOnQueryContinueDrag read FOnDDQueryContinueDrag write FOnDDQueryContinueDrag;    property OnDDChooseEffect: TDDOnChooseEffect read FOnDDChooseEffect write FOnDDChooseEffect;    property OnDDGiveFeedback: TDDOnGiveFeedback read FOnDDGiveFeedback write FOnDDGiveFeedback;    {A drag&drop operation is about to be initiated whith the components window as the source:}    property OnDDDragDetect: TDDOnDragDetect read FOnDDDragDetect write FOnDDDragDetect;    {The component window is the target of a drag&drop operation:}    property OnDDProcessDropped: TOnProcessDropped read FOnDDProcessDropped write FOnDDProcessDropped;    {An error has occurred during a drag&drop operation:}    property OnDDError: TDDErrorEvent read FOnDDError write FOnDDError;    {The drag&drop operation has been executed:}    property OnDDExecuted: TDDExecutedEvent read FOnDDExecuted write FOnDDExecuted;    {Event is fired just before executing the fileoperation. This event is also fired when     files are pasted from the clipboard:}    property OnDDFileOperation: TDDFileOperationEvent read FOnDDFileOperation write FOnDDFileOperation;    {Event is fired after executing the fileoperation. This event is also fired when     files are pasted from the clipboard:}    property OnDDFileOperationExecuted: TDDFileOperationExecutedEvent read FOnDDFileOperationExecuted write FOnDDFileOperationExecuted;    property OnDDCreateDragFileList: TDDOnCreateDragFileList      read FOnDDCreateDragFileList write FOnDDCreateDragFileList;    property OnDDEnd: TNotifyEvent      read FOnDDEnd write FOnDDEnd;    property OnDDCreateDataObject: TDDOnCreateDataObject      read FOnDDCreateDataObject write FOnDDCreateDataObject;    property OnBusy: TDirViewBusy read FOnBusy write FOnBusy;    { Show popupmenu when dropping a file with the right mouse button }    property TargetPopUpMenu: Boolean read GetTargetPopUpMenu write SetTargetPopUpMenu default True;    {Current selected directory:}    property Directory: string read GetDirectory write SetDirectory;    property DragNode: TTreeNode read FDragNode;    property LastDDResult: TDragResult read FLastDDResult;  end;implementationuses  SysUtils, ShellApi, ImgList, ActiveX, Math,  IEListView, BaseUtils;constructor TCustomDriveView.Create(AOwner: TComponent);begin  inherited;  DragMode := dmAutomatic;  FDragFileList := TStringList.Create;  FDragDrive := '';  FExeDrag := False;  FDDLinkOnExeDrag := True;  FContextMenu := False;  FCanChange := True;  FUseSystemContextMenu := True;  FNaturalOrderNumericalSorting := True;  FDarkMode := False;  FRecreatingHandle := False;  OnCompare := DoCompare;  FDragDropFilesEx := TCustomizableDragDropFilesEx.Create(Self);  with FDragDropFilesEx do  begin    AcceptOwnDnd := True;    {MP}    AutoDetectDnD := False;    {/MP}    BringToFront := True;    CompleteFileList := True;    NeedValid := [nvFileName];    RenderDataOn := rdoEnterAndDropSync;    TargetPopUpMenu := True;    OnDragEnter := DDDragEnter;    OnDragLeave := DDDragLeave;    OnDragOver := DDDragOver;    OnDrop := DDDrop;    OnQueryContinueDrag := DDQueryContinueDrag;    OnSpecifyDropTarget := DDSpecifyDropTarget;    OnDropHandlerSucceeded := DDDropHandlerSucceeded;    OnGiveFeedback := DDGiveFeedback;    OnProcessDropped := DDProcessDropped;    OnDragDetect := DDDragDetect;  end;  OnCustomDrawItem := InternalOnDrawItem;  FScrollOnDragOver := TTreeViewScrollOnDragOver.Create(Self, False);  FScrollOnDragOver.OnBeforeUpdate := ScrollOnDragOverBeforeUpdate;  FScrollOnDragOver.OnAfterUpdate := ScrollOnDragOverAfterUpdate;end;destructor TCustomDriveView.Destroy;begin  FreeAndNil(FScrollOnDragOver);  FreeAndNil(FImageList);  if Assigned(Images) then    Images.Free;  if Assigned(FDragImageList) then  begin    if GlobalDragImageList = FDragImageList then      GlobalDragImageList := nil;    FDragImageList.Free;  end;  FDragFileList.Destroy;  if Assigned(FDragDropFilesEx) then    FDragDropFilesEx.Free;  inherited Destroy;end;procedure TCustomDriveView.UpdateItemHeight;var  ImageHeight: Integer;  TextHeight: Integer;begin  // Particularly when called from CMFontChanged because we are changing (reverting)  // the default form font, the Images might not be set up yet  if Assigned(Images) then    ImageHeight := (Images.Width * 9) div 8  else    ImageHeight := 0;  // 16 seems to be the system default tree view item height  TextHeight := ScaleByControlTextHeightRunTime(Canvas, 16);  TreeView_SetItemHeight(Handle, Max(ImageHeight, TextHeight));end;procedure TCustomDriveView.CMFontChanged(var Message: TMessage);begin  inherited;  UpdateItemHeight;end;procedure TCustomDriveView.NeedImageLists;var  AImages: TImageList;begin  if not Assigned(Images) then  begin    Images := TImageList.Create(Self);  end;  AImages := ShellImageListForControl(Self, ilsSmall);  if Images.Handle <> AImages.Handle then  begin    // When assigned directly (as in TCustomDirView), when moving from low to high DPI display,    // the images are resized vertically two times (thoguh originally, this approach was likely taken    // for different reasons)    Images.Handle := AImages.Handle;    Images.ShareImages := AImages.ShareImages;    Images.DrawingStyle := AImages.DrawingStyle;    if Assigned(FImageList) then      FImageList.Free;    FImageList := OverlayImageList(Images.Width);  end;  UpdateItemHeight;end;procedure TCustomDriveView.CMDPIChanged(var Message: TMessage);begin  inherited;  NeedImageLists;end;procedure TCustomDriveView.ChangeScale(M, D: Integer; isDpiChange: Boolean);begin  inherited;  // WORKAROUND  // The Indent seems to be scaled by Windows.  // The TCustomTreeView.ChangeScale redundantly scales it again when Images.IsScaled  // (and we need Images.IsScaled, otherwise TCustomTreeView enables DPI [pixel] scaling)  // But we cannot just revert the scaling, because it is needed when DPI changes on runtime.  // (strangelly for plain tree view [e.g. navigation tree on preferences dialog, it works correctly,  // so it seems that Windows scales Ident on runtime only when the tree have images -  // what is confirmed by double scaling on Login dialog - so there we should do the same trick)  Indent := ScaleByCurrentPPI(19, Self);end;procedure TCustomDriveView.CreateWnd;begin  inherited;  if DarkMode then AllowDarkModeForWindow(Self, DarkMode);  NeedImageLists;  if not (csDesigning in ComponentState) then    FDragImageList := TDragImageList.Create(Self);  if not Assigned(GlobalDragImageList) then    GlobalDragImageList := FDragImageList;  FDragDropFilesEx.DragDropControl := Self;  FParentForm := GetParentForm(Self);end;procedure TCustomDriveView.Notification(AComponent: TComponent; Operation: TOperation);begin  inherited;  if Operation = opRemove then  begin    if AComponent = GetCustomDirView then SetCustomDirView(nil);  end;end;procedure TCustomDriveView.InternalOnDrawItem(Sender: TCustomTreeView; Node: TTreeNode;  State: TCustomDrawState; var DefaultDraw: Boolean);var  FItemColor: TColor;begin  if Assigned(Node) and Assigned(Node.Data) and (Node <> DropTarget) then  begin    if not Node.Selected then    begin      FItemColor := NodeColor(Node);      if (FItemColor <> clDefaultItemColor) and         (Canvas.Font.Color <> FItemColor) then           Canvas.Font.Color := FItemColor;    end      else    if (not Self.Focused) and HideSelection then    begin      Canvas.Brush.Color := clBtnFace;      Canvas.Font.Color := clBtnText;    end      else    if Node.Selected and (not Self.Focused) and DarkMode then    begin      Canvas.Font.Color := Font.Color;    end;  end;end; {InternalOnDrawItem}procedure TCustomDriveView.ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);var  NodeToValidate: TTreeNode;begin  GlobalDragImageList.HideDragImage;  if Assigned(ObjectToValidate) then  begin    NodeToValidate := (ObjectToValidate as TTreeNode);    if not NodeToValidate.HasChildren then      ValidateDirectory(NodeToValidate);  end;end;procedure TCustomDriveView.ScrollOnDragOverAfterUpdate;begin  GlobalDragImageList.ShowDragImage;end;procedure TCustomDriveView.DDDragEnter(DataObj: IDataObject; KeyState: Longint;  Point: TPoint; var Effect: Longint; var Accept: Boolean);var  Index: Integer;begin  if (FDragDropFilesEx.FileList.Count > 0) and     (Length(TFDDListItem(FDragDropFilesEx.FileList[0]^).Name) > 0) Then  begin    try      FDragDrive := DriveInfo.GetDriveKey(TFDDListItem(FDragDropFilesEx.FileList[0]^).Name);    except      // WinRAR gives us only filename on "enter", we get a full path only on "drop".      FDragDrive := '';    end;    FExeDrag := FDDLinkOnExeDrag and      (deLink in DragDropFilesEx.TargetEffects) and      ((DragDropFilesEx.AvailableDropEffects and DROPEFFECT_LINK) <> 0);    if FExeDrag then    begin      for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do        if not IsExecutable(TFDDListItem(FDragDropFilesEx.FileList[Index]^).Name) then        begin          FExeDrag := False;          Break;        end;    end;  end    else  begin    FDragDrive := '';  end;  FScrollOnDragOver.StartDrag;  if Assigned(FOnDDDragEnter) then    FOnDDDragEnter(Self, DataObj, KeyState, Point, Effect, Accept);end; {DDDragEnter}procedure TCustomDriveView.DDDragLeave(Dummy: Integer);begin  if Assigned(DropTarget) then  begin    if GlobalDragImageList.Dragging then      GlobalDragImageList.HideDragImage;    DropTarget := nil;    Update;  end;  if Assigned(FOnDDDragLeave) then    FOnDDDragLeave(Self);end; {DragLeave}procedure TCustomDriveView.DDDragOver(KeyState: Longint; Point: TPoint; var Effect: Longint; PreferredEffect: Longint);var  Node: TTreeNode;  Rect1: TRect;  UpdateImage: Boolean;  LastDragNode: TTreeNode;begin  if Effect <> DROPEFFECT_NONE then  begin    Node := GetNodeAt(Point.X, Point.Y);    if Assigned(Node) then    begin      LastDragNode := DropTarget;      UpdateImage := False;      if GlobalDragImageList.Dragging and (LastDragNode <> Node) then      begin        if Assigned(LastDragNode) then        begin          Rect1 := LastDragNode.DisplayRect(True);          if Rect1.Right >= Point.x - GlobalDragImageList.GetHotSpot.X then          begin            GlobalDragImageList.HideDragImage;            UpdateImage := True;          end            else          begin            Rect1 := Node.DisplayRect(True);            if Rect1.Right >= Point.x - GlobalDragImageList.GetHotSpot.X then            begin              GlobalDragImageList.HideDragImage;              UpdateImage := True;            end          end;        end          else        begin          {LastDragNode not assigned:}          GlobalDragImageList.HideDragImage;          UpdateImage := True;        end;      end;      DropTarget := Node;      if UpdateImage then        GlobalDragImageList.ShowDragImage;      {Drop-operation allowed at this location?}      if Assigned(FDragNode) and         (Effect <> DROPEFFECT_LINK) and         ((Node = FDragNode) or Node.HasAsParent(FDragNode) or (FDragNode.Parent = Node)) then         Effect := DROPEFFECT_NONE;      FScrollOnDragOver.DragOver(Point);    end {Assigned(Node)}      else    begin      DropTarget := nil;    end;  end;  DDChooseEffect(KeyState, Effect, PreferredEffect);  if Assigned(FOnDDDragOver) then    FOnDDDragOver(Self, KeyState, Point, Effect);  if not Assigned(DropTarget) then Effect := DROPEFFECT_NONE    else  if NodeIsRecycleBin(DropTarget) then  begin    if FDragDropFilesEx.FileNamesAreMapped then Effect := DROPEFFECT_NONE      else Effect := DROPEFFECT_MOVE;  end;end; {DDDragOver}procedure TCustomDriveView.DDDrop(DataObj: IDataObject; KeyState: Longint;  Point: TPoint; var Effect: Longint);begin  if GlobalDragImageList.Dragging then    GlobalDragImageList.HideDragImage;  if Effect = DROPEFFECT_NONE then    DropTarget := nil;  if Assigned(FOnDDDrop) then    FOnDDDrop(Self, DataObj, KeyState, Point, Effect);end; {DDDrop}procedure TCustomDriveView.DDQueryContinueDrag(EscapePressed: BOOL; KeyState: Longint;  var Result: HResult);var  Point: TPoint;  ClientPoint: TPoint;  KnowTime: FILETIME;begin  if Result = DRAGDROP_S_DROP then  begin    GetSystemTimeAsFileTime(KnowTime);    if ((Int64(KnowTime) - Int64(FDragStartTime)) <= DDDragStartDelay) then      Result := DRAGDROP_S_CANCEL;  end;  if Assigned(FOnDDQueryContinueDrag) then    FOnDDQueryContinueDrag(Self, EscapePressed, KeyState, Result);  if EscapePressed then  begin    if GlobalDragImageList.Dragging then      GlobalDragImageList.HideDragImage;    DropTarget := nil;  end    else  begin    if GlobalDragImageList.Dragging then    begin      GetCursorPos(Point);      {Convert screen coordinates to the parentforms coordinates:}      ClientPoint := FParentForm.ScreenToClient(Point);      {Move the drag image to the new position and show it:}      if not CompareMem(@ClientPoint, @FDragPos, SizeOf(TPoint)) then      begin        FDragPos := ClientPoint;        if PtInRect(FParentForm.BoundsRect, Point) then        begin          GlobalDragImageList.DragMove(ClientPoint.X, ClientPoint.Y);          GlobalDragImageList.ShowDragImage;        end          else GlobalDragImageList.HideDragImage;      end;    end;  end;end; {DDQueryContinueDrag}procedure TCustomDriveView.DDDropHandlerSucceeded(Sender: TObject;  KeyState: Integer; Point: TPoint; Effect: Integer);begin  DropTarget := nil;end;procedure TCustomDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);begin  if Assigned(FOnDDChooseEffect) then    FOnDDChooseEffect(Self, KeyState, Effect);end;procedure TCustomDriveView.DDGiveFeedback(Effect: Longint; var Result: HResult);begin  if Assigned(FOnDDGiveFeedback) then    FOnDDGiveFeedback(Self, Effect, Result);end; {DDGiveFeedback}procedure TCustomDriveView.DDProcessDropped(Sender: TObject; KeyState: Longint;  Point: TPoint; Effect: Longint);begin  try    if Assigned(DropTarget) then    try      if NodePathExists(DropTarget) then      begin        if Assigned(FOnDDProcessDropped) then          FOnDDProcessDropped(Self, KeyState, Point, Effect);        if Effect <> DROPEFFECT_NONE then        begin          PerformDragDropFileOperation(DropTarget, Effect);          if Assigned(FOnDDExecuted) then            FOnDDExecuted(Self, Effect);        end;      end        else      begin        ValidateDirectory(DropTarget);        DDError(DDPathNotFoundError);      end;    finally      DropTarget := nil;      ClearDragFileList(FDragDropFilesEx.FileList);    end;  except    Application.HandleException(Self);  end;end; {ProcessDropped}procedure TCustomDriveView.DDError(Error: TDDError);begin  if Assigned(FOnDDError) then FOnDDError(Self, Error)    else raise Exception.CreateFmt(SDragDropError, [Ord(Error)]);end; {DDError}procedure TCustomDriveView.DDSpecifyDropTarget(Sender: TObject;  DragDropHandler: Boolean; Point: TPoint; var PIDL: PItemIDList; var Filename: string);begin  PIDL := nil;  if DragDropHandler and Assigned(DropTarget) then FileName := NodePathName(DropTarget)    else FileName := EmptyStr;end; {DDSpecifyDropTarget}procedure TCustomDriveView.DDDragDetect(KeyState: Longint; DetectStart, Point: TPoint;  DragStatus: TDragDetectStatus);var  P: TPoint;  ImageList: HImageList;  NodeRect: TRect;  FileListCreated: Boolean;  AvoidDragImage: Boolean;begin  if (DragStatus = ddsDrag) and (not Assigned(FDragNode)) then  begin    P := ScreenToClient(FStartPos);    FDragNode := GetNodeAt(P.X, P.Y);  end;  if Assigned(FOnDDDragDetect) then    FOnDDDragDetect(Self, KeyState, DetectStart, Point, DragStatus);  if (DragStatus = ddsDrag) and Assigned(FDragNode) then  begin    NodeRect := FDragNode.DisplayRect(True);    Dec(NodeRect.Left, 16);    {Check, wether the mouse cursor was within the nodes display rectangle:}    if (NodeRect.Left > P.X) or (NodeRect.Right < P.X) or       (not NodeCanDrag(FDragNode)) then    begin      FDragNode := nil;      Exit;    end;    FDragDrive := '';    ClearDragFileList(FDragDropFilesEx.FileList);    FDragDropFilesEx.CompleteFileList := DragCompleteFileList;    FileListCreated := False;    AvoidDragImage := False;    if Assigned(OnDDCreateDragFileList) then    begin      OnDDCreateDragFileList(Self, FDragDropFilesEx.FileList, FileListCreated);      if FileListCreated then        AvoidDragImage := True;    end;    if not FileListCreated then    begin      AddToDragFileList(FDragDropFilesEx.FileList, FDragNode);    end;    FDragDropFilesEx.SourceEffects := DDSourceEffects;    if FDragDropFilesEx.FileList.Count > 0 then    try      {Create the dragimage:}      GlobalDragImageList := FDragImageList;      if not AvoidDragImage then      begin        {Hide the selection mark to get a proper dragimage:}        if Selected = FDragNode then          Selected := nil;        ImageList := TreeView_CreateDragImage(Handle, FDragNode.ItemID);        {Show the selection mark if it was hidden:}        if not Assigned(Selected) then          Selected := FDragNode;        if ImageList <> Invalid_Handle_Value then        begin          GlobalDragImageList.Handle := ImageList;          GlobalDragImageList.SetDragImage(0, P.X - NodeRect.TopLeft.X, P.Y - NodeRect.TopLeft.Y);          P := FParentForm.ScreenToClient(Point);          GlobalDragImageList.BeginDrag(FParentForm.Handle, P.X, P.Y);          GlobalDragImageList.HideDragImage;          ShowCursor(True);        end;      end;      DropSourceControl := Self;      GetSystemTimeAsFileTime(FDragStartTime);      {Supress the context menu:}      FContextMenu := False;      {Execute the drag&drop-Operation:}      FLastDDResult := DDExecute;      {the drag&drop operation is finished, so clean up the used drag image:}      GlobalDragImageList.EndDrag;      GlobalDragImageList.Clear;      Application.ProcessMessages;    finally      ClearDragFileList(FDragDropFilesEx.FileList);      FDragDrive := '';      DropTarget := nil;      try        if Assigned(OnDDEnd) then          OnDDEnd(Self);      finally        DropSourceControl := nil;        FDragNode := nil;      end;    end;  end;end; {(DDDragDetect}function TCustomDriveView.DDExecute: TDragResult;var  DataObject: TDataObject;begin  DataObject := nil;  if Assigned(OnDDCreateDataObject) then    OnDDCreateDataObject(Self, DataObject);  Result := FDragDropFilesEx.Execute(DataObject);end;function TCustomDriveView.GetNodeFromHItem(Item: TTVItem): TTreeNode;begin  Result := nil;  if Items <> nil then    with Item do      if (state and TVIF_PARAM) <> 0 then        Result := Pointer(lParam)      else        Result := Items.GetNode(hItem);end; {GetNodeFromItem}function TCustomDriveView.IsCustomDrawn(Target: TCustomDrawTarget;  Stage: TCustomDrawStage): Boolean;begin  Result := inherited IsCustomDrawn(Target, Stage) or    ((Target = dtItem) and (Stage = cdPostPaint));end;function TCustomDriveView.CustomDrawItem(Node: TTreeNode; State: TCustomDrawState;  Stage: TCustomDrawStage; var PaintImages: Boolean): Boolean;var  Point: TPoint;  Index: Integer;  OverlayIndexes: Word;  OverlayIndex: Word;  Image: Word;begin  Result := inherited CustomDrawItem(Node, State, Stage, PaintImages);  if Result and (Stage = cdPostPaint) then  begin    Assert(Assigned(Node));    OverlayIndexes := NodeOverlayIndexes(Node);    OverlayIndex := 1;    while OverlayIndexes > 0 do    begin      if (OverlayIndex and OverlayIndexes) <> 0 then      begin        Index := 0;        Image := OverlayIndex;        while Image > 1 do        begin          Inc(Index);          Image := Image shr 1;        end;        Point := Node.DisplayRect(True).TopLeft;        Dec(Point.X, Indent);        ImageList_Draw(ImageList.Handle, Index, Self.Canvas.Handle,          Point.X, Point.Y, ILD_TRANSPARENT);        Dec(OverlayIndexes, OverlayIndex);      end;      OverlayIndex := OverlayIndex shl 1;    end;  end;end;procedure TCustomDriveView.CNNotify(var Msg: TWMNotify);begin  case Msg.NMHdr.code of    TVN_BEGINDRAG:  DDDragDetect(MK_LBUTTON, FStartPos, Mouse.CursorPos, ddsDrag);    TVN_BEGINRDRAG: DDDragDetect(MK_RBUTTON, FStartPos, Mouse.CursorPos, ddsDrag);  else    inherited;  end;end; {CNNotify}procedure TCustomDriveView.CMColorChanged(var Msg: TMessage);begin  inherited;  if Assigned(Images) then    Images.BkColor := Color;  ForceColorChange(Self);end;procedure TCustomDriveView.WMLButtonDown(var Msg: TWMLButtonDown);begin  if not IsBusy then  begin    FCanChange := False;    GetCursorPos(FStartPos);    inherited;  end;end; {WMLButtonDown}procedure TCustomDriveView.WMLButtonUp(var Msg: TWMLButtonDown);begin  FCanChange := True;  if Assigned(DropTarget) and Assigned(DropTarget.Data) then    Selected := DropTarget;  DropTarget := nil;  inherited;end; {WMLButtonUp}procedure TCustomDriveView.WMRButtonDown(var Msg: TWMRButtonDown);begin  if not IsBusy then  begin    GetCursorPos(FStartPos);    if FDragDropFilesEx.DragDetectStatus <> ddsDrag then      FContextMenu := True;    inherited;  end;end; {WMRButtonDown}procedure TCustomDriveView.WMLButtonDblClk(var Message: TWMLButtonDblClk);begin  if not IsBusy then  begin    inherited;  end;end;procedure TCustomDriveView.WMContextMenu(var Msg: TWMContextMenu);var  Node: TTreeNode;  Point: TPoint;  PrevAutoPopup: Boolean;begin  // Not sure what is this exactly for, as without AutoPopup, the inherited WMContextMenu is almost noop.  // In general it would be better to override DoContextPopup  PrevAutoPopup := False;  try    if Assigned(PopupMenu) then    begin      PrevAutoPopup := PopupMenu.AutoPopup;      PopupMenu.AutoPopup := False;    end;    inherited;  finally    if Assigned(PopupMenu) then      PopupMenu.AutoPopup := PrevAutoPopup;  end;  FStartPos.X := -1;  FStartPos.Y := -1;  try    if FContextMenu then    begin      Point.X := Msg.XPos;      Point.Y := Msg.YPos;      Point := ScreenToClient(Point);      Node := GetNodeAt(Point.X, Point.Y);      if FUseSystemContextMenu and Assigned(Node) then      begin        if Assigned(OnMouseDown) then          OnMouseDown(Self, mbRight, [], Msg.XPos, Msg.YPos);        DisplayContextMenu(Node, Mouse.CursorPos);      end        else      begin        if Assigned(PopupMenu) then          PopupMenu.Popup(Msg.XPos, Msg.YPos);      end;    end;    FContextMenu := False;  finally    DropTarget := nil;  end;end; {WMContextMenu}// Might not be always called, see comment in TDriveView.DestroyWndprocedure TCustomDriveView.CMRecreateWnd(var Msg: TMessage);var  HadHandle: Boolean;begin  HadHandle := HandleAllocated;  inherited;  // If the control is not showing (e.g. because the machine is locked), the handle is not recreated.  // If contents is reloaded (LoadPath) without handle allocated, it crashes  // (as the handle is implicitly created somewhere in the middle of the reload and chaos ensures).  if HadHandle then  begin    Assert(not FRecreatingHandle);    FRecreatingHandle := True;    try      HandleNeeded;    finally      FRecreatingHandle := False;    end;  end;end;procedure TCustomDriveView.Delete(Node: TTreeNode);begin  if Node = FDragNode then    FDragNode := nil;  if Node = DropTarget then  begin    DropTarget := nil;    Update;  end;  inherited;end; {OnDelete}procedure TCustomDriveView.WMKeyDown(var Message: TWMKeyDown);begin  if not IsBusy then  begin    inherited;  end;end;procedure TCustomDriveView.KeyDown(var Key: Word; Shift: TShiftState);begin  if (Key = VK_RETURN) and (ssAlt in Shift) and (not IsEditing) and     Assigned(Selected) then  begin    DisplayPropertiesMenu(Selected);    Key := 0;  end;  inherited;end; {KeyDown}procedure TCustomDriveView.KeyPress(var Key : Char);begin  if Assigned(Selected) then  begin    if not IsEditing then    begin      case Key of        #13, ' ':          begin            Selected.Expanded := not Selected.Expanded;            Key := #0;          end;        '/':          begin            Selected.Collapse(True);            Selected.MakeVisible;            Key := #0;          end;        '*':          Selected.MakeVisible;      end {Case}    end  end;  inherited;end; {KeyPress}procedure TCustomDriveView.KeyUp(var Key: Word; Shift: TShiftState);var  Point: TPoint;begin  inherited;  if (Key = VK_APPS) and Assigned(Selected) then  begin    Point := ClientToScreen(Selected.DisplayRect(True).TopLeft);    Inc(Point.Y, 20);    DisplayContextMenu(Selected, Point);  end;end; {KeyUp}procedure TCustomDriveView.ValidateDirectory(Node: TTreeNode);begin  ValidateDirectoryEx(Node, rsRecursiveExisting, False);end;  {ValidateDirectory}function TCustomDriveView.DoCompareText(Text1, Text2: string): Integer;begin  Result := CompareLogicalTextPas(Text1, Text2, NaturalOrderNumericalSorting);end;procedure TCustomDriveView.DoCompare(Sender: TObject; Node1, Node2: TTreeNode; Data: Integer; var Compare: Integer);begin  Compare := DoCompareText(Node1.Text, Node2.Text);end;function TCustomDriveView.SortChildren(ParentNode: TTreeNode; Recurse: Boolean): Boolean;begin  Result := Assigned(ParentNode) and ParentNode.AlphaSort(Recurse);end; {SortChildren}function TCustomDriveView.IterateSubTree(var StartNode : TTreeNode;  CallBackFunc: TCallBackFunc; Recurse: TRecursiveScan;  ScanStartNode: TScanStartNode; Data: Pointer): Boolean;  function ScanSubTree(var StartNode: TTreeNode): Boolean;  var    Node: TTreeNode;    NextNode: TTreeNode;    NodeHasChilds: Boolean;  begin    Result := False;    if not Assigned(StartNode) then Exit;    Node := StartNode.GetFirstChild;    while Assigned(Node) do    begin      NextNode := StartNode.GetNextChild(Node);      NodeHasChilds := Node.HasChildren;      if not CallBackFunc(Node, Data) then Exit;      if Assigned(Node) and         (Recurse = rsRecursiveExisting) and NodeHasChilds then      begin        if not ScanSubTree(Node) then Exit;      end;      Node := NextNode;    end;    Result := True;  end; {ScanSubTree}begin {IterateSubTree}  Result := False;  if Assigned(CallBackFunc) then  begin    if ScanStartNode = coScanStartNode then    begin      CallBackFunc(StartNode, Data);    end;    if (not Assigned(StartNode)) or       ScanSubTree(StartNode) then    begin      Result := True;    end;  end;end; {IterateSubTree}procedure TCustomDriveView.ClearDragFileList(FileList: TFileList);begin  FileList.Clear;end;procedure TCustomDriveView.AddToDragFileList(FileList: TFileList; Node: TTreeNode);begin  FileList.AddItem(nil, NodePathName(Node));end;function TCustomDriveView.NodeCanDrag(Node: TTreeNode): Boolean;begin  Result := True;end;function TCustomDriveView.NodeOverlayIndexes(Node: TTreeNode): Word;begin  Result := oiNoOverlay;end;function TCustomDriveView.NodeIsRecycleBin(Node: TTreeNode): Boolean;begin  Result := False;end;function TCustomDriveView.NodePathExists(Node: TTreeNode): Boolean;begin  Result := True;end;procedure TCustomDriveView.SetDimmHiddenDirs(Value: Boolean);begin  if Value <> FDimmHiddenDirs then  begin    FDimmHiddenDirs := Value;    Self.Invalidate;  end;end; {SetDimmHiddenDirs}procedure TCustomDriveView.SetShowHiddenDirs(Value: Boolean);begin  if Value <> FShowHiddenDirs then  begin    FShowHiddenDirs := Value;    RebuildTree;  end;end; {SetDimmHiddenDirs}procedure TCustomDriveView.SetNaturalOrderNumericalSorting(Value: Boolean);begin  if NaturalOrderNumericalSorting <> Value then  begin    FNaturalOrderNumericalSorting := Value;    AlphaSort;  end;end;procedure TCustomDriveView.SetDarkMode(Value: Boolean);begin  if DarkMode <> Value then  begin    FDarkMode := Value;    // See TCustomDirView.SetDarkMode    if HandleAllocated then AllowDarkModeForWindow(Self, DarkMode);  end;end;function TCustomDriveView.GetTargetPopupMenu: Boolean;begin  if Assigned(FDragDropFilesEx) then Result := FDragDropFilesEx.TargetPopupMenu    else Result := True;end;procedure TCustomDriveView.SetTargetPopUpMenu(Value: Boolean);begin  if Assigned(FDragDropFilesEx) then    FDragDropFilesEx.TargetPopupMenu := Value;end; {SetTargetPopUpMenu}function TCustomDriveView.GetDirectory: string;begin  if Assigned(Selected) then Result := NodePathName(Selected)    else Result := '';end; {GetDirectory}procedure TCustomDriveView.SetDirectory(Value: string);var  NewSelected: TTreeNode;begin  NewSelected := FindPathNode(Value);  if Assigned(NewSelected) and (NewSelected <> Selected) then  begin    FCanChange := True;    NewSelected.MakeVisible;    Selected := NewSelected;  end    else  if csDesigning in ComponentState then    Selected := nil;end; {SetDirectory}function TCustomDriveView.DoBusy(Busy: Integer): Boolean;begin  Result := True;  if Assigned(OnBusy) then  begin    OnBusy(Self, Busy, Result);  end;end;function TCustomDriveView.StartBusy: Boolean;begin  Result := DoBusy(1);end;function TCustomDriveView.IsBusy: Boolean;begin  Result := DoBusy(0);end;procedure TCustomDriveView.EndBusy;begin  DoBusy(-1);end;end.
 |