DriveView.pas 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739
  1. unit DriveView;
  2. {==================================================================
  3. Component TDriveView / Version 2.6, January 2000
  4. ==================================================================
  5. Description:
  6. ============
  7. Displays the the directory structure of all drives as treeview
  8. with shell icons. Complete drag&Drop support for files and
  9. directories.
  10. Author:
  11. =======
  12. (c) Ingo Eckel 1998, 1999
  13. Sodener Weg 38
  14. 65812 Bad Soden
  15. Germany
  16. Modifications (for WinSCP):
  17. ===========================
  18. (c) Martin Prikryl 2004
  19. V2.6:
  20. - Shows "shared"-symbol with directories
  21. - Delphi5 compatible
  22. For detailed documentation and history see TDriveView.htm.
  23. {==================================================================}
  24. interface
  25. { Define ENHVALIDATE to scan all existing directories on a detected filesystem change:}
  26. {.$DEFINE ENHVALIDATE}
  27. {Required compiler options for TDriveView:}
  28. {$A+,B-,X+,H+,P+}
  29. {$WARN SYMBOL_PLATFORM OFF}
  30. uses
  31. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ComObj,
  32. Dialogs, ComCtrls, ShellApi, CommCtrl, ExtCtrls, ActiveX, ShlObj,
  33. DirView, ShellDialogs, DragDrop, DragDropFilesEx, FileChanges, FileOperator,
  34. DiscMon, IEDriveInfo, IEListView, BaseUtils, CustomDirView,
  35. CustomDriveView, System.Generics.Collections;
  36. const
  37. msThreadChangeDelay = 50;
  38. ErrorNodeNA = '%s: Node not assigned';
  39. {Flags used by TDriveView.RefreshRootNodes:}
  40. dvdsFloppy = 8; {Include floppy drives}
  41. dvdsRereadAllways = 16; {Refresh drivestatus in any case}
  42. WM_USER_SHCHANGENOTIFY = WM_USER + $2000 + 13;
  43. type
  44. EInvalidDirName = class(Exception);
  45. ENodeNotAssigned = class(Exception);
  46. TDriveStatus = class
  47. Scanned: Boolean; {Drive allready scanned?}
  48. Verified: Boolean; {Drive completly scanned?}
  49. RootNode: TTreeNode; {Rootnode to drive}
  50. RootNodeIndex: Integer;
  51. DiscMonitor: TDiscMonitor; {Monitor thread}
  52. ChangeTimer: TTimer; {Change timer for the monitor thread}
  53. DefaultDir: string; {Current directory}
  54. DriveHandle: THandle;
  55. NotificationHandle: HDEVNOTIFY;
  56. end;
  57. TDriveStatusPair = TPair<string, TDriveStatus>;
  58. TScanDirInfo = record
  59. SearchNewDirs: Boolean;
  60. StartNode: TTreeNode;
  61. DriveType: Integer;
  62. end;
  63. PScanDirInfo = ^TScanDirInfo;
  64. TDriveView = class;
  65. TNodeData = class
  66. private
  67. FDirName: string;
  68. FAttr: Integer;
  69. FScanned: Boolean;
  70. FData: Pointer;
  71. FIsRecycleBin: Boolean;
  72. FIconEmpty: Boolean;
  73. public
  74. constructor Create;
  75. destructor Destroy; override;
  76. property DirName: string read FDirName write FDirName;
  77. property Attr: Integer read FAttr write FAttr;
  78. property Scanned: Boolean read FScanned write FScanned;
  79. property Data: Pointer read FData write FData;
  80. property IsRecycleBin: Boolean read FIsRecycleBin;
  81. property IconEmpty: Boolean read FIconEmpty write FIconEmpty;
  82. end;
  83. TDriveTreeNode = class(TTreeNode)
  84. procedure Assign(Source: TPersistent); override;
  85. end;
  86. TDriveViewRefreshDrives = procedure(Sender: TObject; Global: Boolean) of object;
  87. TDriveViewContinueLoading = procedure(Sender: TObject; var Start: TDateTime; Path: string; Count: Integer; var Stop: Boolean) of object;
  88. TDriveView = class(TCustomDriveView)
  89. private
  90. FDriveStatus: TObjectDictionary<string, TDriveStatus>;
  91. FConfirmDelete: Boolean;
  92. FConfirmOverwrite: Boolean;
  93. FWatchDirectory: Boolean;
  94. FDirectory: string;
  95. FShowVolLabel: Boolean;
  96. FVolDisplayStyle: TVolumeDisplayStyle;
  97. FChangeFlag: Boolean;
  98. FLastDir: string;
  99. FValidateFlag: Boolean;
  100. FCreating: Boolean;
  101. FForceRename: Boolean;
  102. FRenameNode: TTreeNode;
  103. FLastRenameName: string;
  104. FInternalWindowHandle: HWND;
  105. FChangeNotify: ULONG;
  106. FPrevSelected: TTreeNode;
  107. FPrevSelectedIndex: Integer;
  108. FChangeTimerSuspended: Integer;
  109. {Additional events:}
  110. FOnDisplayContextMenu: TNotifyEvent;
  111. FOnRefreshDrives: TDriveViewRefreshDrives;
  112. FOnNeedHiddenDirectories: TNotifyEvent;
  113. FOnContinueLoading: TDriveViewContinueLoading;
  114. {used components:}
  115. FDirView: TDirView;
  116. FFileOperator: TFileOperator;
  117. FChangeInterval: Cardinal;
  118. {Drag&drop:}
  119. FLastPathCut: string;
  120. {Drag&drop helper functions:}
  121. procedure SignalDirDelete(Sender: TObject; Files: TStringList);
  122. function GetSubDir(var SRec: TSearchRec): Boolean;
  123. function FindFirstSubDir(Path: string; var SRec: TSearchRec): Boolean;
  124. function FindNextSubDir(var SRec: TSearchRec): Boolean;
  125. function ReadSubDirs(Node: TTreeNode; DriveType: Integer; SpecificFile: string = ''): Boolean;
  126. {Callback-functions used by iteratesubtree:}
  127. function CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  128. procedure DeleteNode(Node: TTreeNode);
  129. { Notification procedures used by component TDiscMonitor: }
  130. procedure ChangeDetected(Sender: TObject; const Directory: string;
  131. var SubdirsChanged: Boolean);
  132. procedure ChangeInvalid(Sender: TObject; const Directory: string; const ErrorStr: string);
  133. {Notification procedure used by component TTimer:}
  134. procedure ChangeTimerOnTimer(Sender: TObject);
  135. protected
  136. procedure SetSelected(Node: TTreeNode);
  137. procedure SetWatchDirectory(Value: Boolean);
  138. procedure SetShowVolLabel(ShowIt: Boolean);
  139. procedure SetDirView(Value: TDirView);
  140. procedure SetDirectory(Value: string); override;
  141. function DoScanDir(FromNode: TTreeNode): Boolean;
  142. function AddChildNode(ParentNode: TTreeNode; ParentPath: string; SRec: TSearchRec): TTreeNode;
  143. procedure CreateWatchThread(Drive: string);
  144. function NodeWatched(Node: TTreeNode): Boolean;
  145. procedure TerminateWatchThread(Drive: string);
  146. function WatchThreadActive: Boolean; overload;
  147. function WatchThreadActive(Drive: string): Boolean; overload;
  148. procedure InternalWndProc(var Msg: TMessage);
  149. procedure UpdateDriveNotifications(Drive: string);
  150. procedure DriveRemoved(Drive: string);
  151. procedure DriveRemoving(Drive: string);
  152. procedure CancelDriveRefresh;
  153. procedure DoRefreshDrives(Global: Boolean);
  154. procedure RefreshRootNodes(dsFlags: Integer);
  155. function DirAttrMask: Integer;
  156. function CreateDriveStatus: TDriveStatus;
  157. procedure ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  158. NewDirs: Boolean); override;
  159. procedure RebuildTree; override;
  160. procedure SetLastPathCut(Path: string);
  161. function GetCanUndoCopyMove: Boolean; virtual;
  162. procedure CreateWnd; override;
  163. procedure DestroyWnd; override;
  164. procedure Edit(const Item: TTVItem); override;
  165. procedure WMUserRename(var Message: TMessage); message WM_USER_RENAME;
  166. function GetCustomDirView: TCustomDirView; override;
  167. procedure SetCustomDirView(Value: TCustomDirView); override;
  168. function NodePath(Node: TTreeNode): string; override;
  169. function NodeIsRecycleBin(Node: TTreeNode): Boolean; override;
  170. function NodePathExists(Node: TTreeNode): Boolean; override;
  171. function NodeColor(Node: TTreeNode): TColor; override;
  172. function FindPathNode(Path: string): TTreeNode; override;
  173. function DoFindNodeToPath(Path: string; ExistingOnly: Boolean): TTreeNode;
  174. function CreateNode: TTreeNode; override;
  175. function DDSourceEffects: TDropEffectSet; override;
  176. procedure DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer); override;
  177. function DragCompleteFileList: Boolean; override;
  178. function DDExecute: TDragResult; override;
  179. public
  180. property Images;
  181. property StateImages;
  182. property Items stored False;
  183. property Selected Write SetSelected stored False;
  184. property DragImageList: TDragImageList read FDragImageList;
  185. property CanUndoCopyMove: Boolean read GetCanUndoCopyMove;
  186. property DDFileOperator: TFileOperator read FFileOperator;
  187. property LastPathCut: string read FLastPathCut write SetLastPathCut;
  188. function UndoCopyMove: Boolean; dynamic;
  189. procedure EmptyClipboard; dynamic;
  190. function CopyToClipBoard(Node: TTreeNode): Boolean; dynamic;
  191. function CutToClipBoard(Node: TTreeNode): Boolean; dynamic;
  192. function CanPasteFromClipBoard: Boolean; dynamic;
  193. function PasteFromClipBoard(TargetPath: string = ''): Boolean; dynamic;
  194. procedure PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer); override;
  195. {Drive handling:}
  196. function GetDriveStatus(Drive: string): TDriveStatus;
  197. function GetDriveTypetoNode(Node: TTreeNode): Integer; {Returns DRIVE_CDROM etc..}
  198. function GetDriveToNode(Node: TTreeNode): string;
  199. function GetDriveText(Drive: string): string;
  200. procedure ScanDrive(Drive: string);
  201. function GetDrives: TStrings;
  202. procedure ScheduleDriveRefresh;
  203. {Node handling:}
  204. procedure SetImageIndex(Node: TTreeNode); virtual;
  205. function FindNodeToPath(Path: string): TTreeNode;
  206. function TryFindNodeToPath(Path: string): TTreeNode;
  207. function RootNode(Node: TTreeNode): TTreeNode;
  208. function GetDirName(Node: TTreeNode): string;
  209. function GetDisplayName(Node: TTreeNode): string;
  210. function NodePathName(Node: TTreeNode): string; override;
  211. constructor Create(AOwner: TComponent); override;
  212. destructor Destroy; override;
  213. {Menu-handling:}
  214. procedure DisplayContextMenu(Node: TTreeNode; Point: TPoint); override;
  215. procedure DisplayPropertiesMenu(Node: TTreeNode); override;
  216. {Watchthread handling:}
  217. procedure StartWatchThread;
  218. procedure StopWatchThread;
  219. procedure SuspendChangeTimer;
  220. procedure ResumeChangeTimer;
  221. procedure StartAllWatchThreads;
  222. procedure StopAllWatchThreads;
  223. procedure ValidateCurrentDirectoryIfNotMonitoring;
  224. (* Modified Events: *)
  225. procedure GetImageIndex(Node: TTreeNode); override;
  226. function CanEdit(Node: TTreeNode): Boolean; override;
  227. function CanChange(Node: TTreeNode): Boolean; override;
  228. function CanExpand(Node: TTreeNode): Boolean; override;
  229. procedure Delete(Node: TTreeNode); override;
  230. procedure Loaded; override;
  231. procedure KeyPress(var Key: Char); override;
  232. procedure Change(Node: TTreeNode); override;
  233. published
  234. {Additional properties:}
  235. {Current selected directory:}
  236. property Directory;
  237. {Confirm deleting directories:}
  238. property ConfirmDelete: Boolean read FConfirmDelete write FConfirmDelete default True;
  239. {Confirm overwriting directories:}
  240. property ConfirmOverwrite: Boolean read FConfirmOverwrite write FConfirmOverwrite default True;
  241. {Enable automatic update on filesystem changes:}
  242. property WatchDirectory: Boolean read FWatchDirectory write SetWatchDirectory default False;
  243. {Linked component TDirView:}
  244. property DirView: TDirView read FDirView write SetDirView;
  245. {Show the volume labels of drives:}
  246. property ShowVolLabel: Boolean read FShowVolLabel write SetShowVolLabel default True;
  247. {Additional events:}
  248. property OnDisplayContextMenu: TNotifyEvent read FOnDisplayContextMenu
  249. write FOnDisplayContextMenu;
  250. property OnRefreshDrives: TDriveViewRefreshDrives read FOnRefreshDrives write FOnRefreshDrives;
  251. property OnContinueLoading: TDriveViewContinueLoading read FOnContinueLoading write FOnContinueLoading;
  252. property OnBusy;
  253. property DDLinkOnExeDrag;
  254. property TargetPopUpMenu;
  255. property OnDDDragEnter;
  256. property OnDDDragLeave;
  257. property OnDDDragOver;
  258. property OnDDDrop;
  259. property OnDDQueryContinueDrag;
  260. property OnDDGiveFeedback;
  261. property OnDDDragDetect;
  262. property OnDDProcessDropped;
  263. property OnDDError;
  264. property OnDDExecuted;
  265. property OnDDFileOperation;
  266. property OnDDFileOperationExecuted;
  267. property Align;
  268. property Anchors;
  269. property AutoExpand;
  270. property BiDiMode;
  271. property BorderStyle;
  272. property BorderWidth;
  273. property ChangeDelay;
  274. property Color;
  275. property Ctl3D;
  276. property Constraints;
  277. property DoubleBuffered;
  278. {Delphi's drag&drop is not compatible with the OLE windows drag&drop:}
  279. property DragKind;
  280. property DragCursor;
  281. property DragMode Default dmAutomatic;
  282. property OnDragDrop;
  283. property OnDragOver;
  284. property Enabled;
  285. property Font;
  286. property HideSelection;
  287. property HotTrack;
  288. property Indent;
  289. property ParentBiDiMode;
  290. property ParentColor;
  291. property ParentCtl3D;
  292. property ParentDoubleBuffered;
  293. property ParentFont;
  294. property ParentShowHint;
  295. property PopupMenu;
  296. property ReadOnly;
  297. property RightClickSelect;
  298. property RowSelect;
  299. property ShowButtons;
  300. property ShowHint;
  301. property ShowLines;
  302. property TabOrder;
  303. property TabStop default True;
  304. property ToolTips;
  305. property Visible;
  306. property OnChange;
  307. property OnChanging;
  308. property OnClick;
  309. property OnCollapsing;
  310. property OnCollapsed;
  311. property OnCompare;
  312. property OnDblClick;
  313. property OnDeletion;
  314. property OnEdited;
  315. property OnEditing;
  316. property OnEndDock;
  317. property OnEndDrag;
  318. property OnEnter;
  319. property OnExit;
  320. property OnExpanding;
  321. property OnExpanded;
  322. property OnGetImageIndex;
  323. property OnGetSelectedIndex;
  324. property OnKeyDown;
  325. property OnKeyPress;
  326. property OnKeyUp;
  327. property OnMouseDown;
  328. property OnMouseMove;
  329. property OnMouseUp;
  330. property OnStartDock;
  331. property OnStartDrag;
  332. property OnNeedHiddenDirectories: TNotifyEvent read FOnNeedHiddenDirectories write FOnNeedHiddenDirectories;
  333. end;
  334. procedure Register;
  335. implementation
  336. uses
  337. CompThread, PasTools, UITypes, Types, System.Generics.Defaults;
  338. type
  339. PInt = ^Integer;
  340. procedure Register;
  341. begin
  342. RegisterComponents('DriveDir', [TDriveView]);
  343. end; {Register}
  344. constructor TNodeData.Create;
  345. begin
  346. inherited;
  347. FAttr := 0;
  348. FScanned := False;
  349. FDirName := '';
  350. FIsRecycleBin := False;
  351. FIconEmpty := True;
  352. end; {TNodeData.Create}
  353. destructor TNodeData.Destroy;
  354. begin
  355. SetLength(FDirName, 0);
  356. inherited;
  357. end; {TNodeData.Destroy}
  358. { TDriveTreeNode }
  359. procedure TDriveTreeNode.Assign(Source: TPersistent);
  360. var
  361. SourceData: TNodeData;
  362. NewData: TNodeData;
  363. begin
  364. inherited Assign(Source);
  365. if not Deleting and (Source is TTreeNode) then
  366. begin
  367. SourceData := TNodeData(TTreeNode(Source).Data);
  368. NewData := TNodeData.Create();
  369. NewData.DirName := SourceData.DirName;
  370. NewData.Attr := SourceData.Attr;
  371. NewData.Scanned := SourceData.Scanned;
  372. NewData.Data := SourceData.Data;
  373. NewData.FIsRecycleBin := SourceData.FIsRecycleBin;
  374. NewData.IconEmpty := SourceData.IconEmpty;
  375. TTreeNode(Source).Data := NewData;
  376. end;
  377. end;
  378. { TDriveView }
  379. constructor TDriveView.Create(AOwner: TComponent);
  380. var
  381. Drive: TRealDrive;
  382. ChangeNotifyEntry: TSHChangeNotifyEntry;
  383. Dummy: string;
  384. begin
  385. inherited;
  386. FCreating := True;
  387. FDriveStatus := TObjectDictionary<string, TDriveStatus>.Create([doOwnsValues]);
  388. FChangeInterval := MSecsPerSec;
  389. for Drive := FirstDrive to LastDrive do
  390. begin
  391. FDriveStatus.Add(Drive, CreateDriveStatus);
  392. end;
  393. FFileOperator := TFileOperator.Create(Self);
  394. FShowVolLabel := True;
  395. FChangeFlag := False;
  396. FLastDir := EmptyStr;
  397. FValidateFlag := False;
  398. FConfirmDelete := True;
  399. FDirectory := EmptyStr;
  400. FForceRename := False;
  401. FLastRenameName := '';
  402. FRenameNode := nil;
  403. FPrevSelected := nil;
  404. FPrevSelectedIndex := -1;
  405. FChangeTimerSuspended := 0;
  406. FConfirmOverwrite := True;
  407. FLastPathCut := '';
  408. FStartPos.X := -1;
  409. FStartPos.Y := -1;
  410. FDragPos := FStartPos;
  411. FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
  412. // Source: petr.solin 2022-02-25
  413. FChangeNotify := 0;
  414. if SpecialFolderLocation(CSIDL_DESKTOP, Dummy, ChangeNotifyEntry.pidl) then
  415. begin
  416. ChangeNotifyEntry.fRecursive := False;
  417. FChangeNotify :=
  418. SHChangeNotifyRegister(
  419. FInternalWindowHandle, SHCNRF_ShellLevel or SHCNRF_NewDelivery,
  420. SHCNE_RENAMEFOLDER or SHCNE_MEDIAINSERTED or SHCNE_MEDIAREMOVED,
  421. WM_USER_SHCHANGENOTIFY, 1, ChangeNotifyEntry);
  422. end;
  423. with FDragDropFilesEx do
  424. begin
  425. ShellExtensions.DragDropHandler := True;
  426. end;
  427. end; {Create}
  428. destructor TDriveView.Destroy;
  429. var
  430. DriveStatusPair: TDriveStatusPair;
  431. begin
  432. if FChangeNotify <> 0 then SHChangeNotifyDeregister(FChangeNotify);
  433. Classes.DeallocateHWnd(FInternalWindowHandle);
  434. for DriveStatusPair in FDriveStatus do
  435. begin
  436. with DriveStatusPair.Value do
  437. begin
  438. if Assigned(DiscMonitor) then
  439. FreeAndNil(DiscMonitor);
  440. if Assigned(ChangeTimer) then
  441. FreeAndNil(ChangeTimer);
  442. end;
  443. UpdateDriveNotifications(DriveStatusPair.Key);
  444. end;
  445. FDriveStatus.Free;
  446. if Assigned(FFileOperator) then
  447. FFileOperator.Free;
  448. inherited Destroy;
  449. end; {Destroy}
  450. function TDriveView.CreateDriveStatus: TDriveStatus;
  451. begin
  452. Result := TDriveStatus.Create;
  453. with Result do
  454. begin
  455. Scanned := False;
  456. Verified := False;
  457. RootNode := nil;
  458. RootNodeIndex := -1;
  459. DiscMonitor := nil;
  460. DefaultDir := EmptyStr;
  461. {ChangeTimer: }
  462. ChangeTimer := TTimer.Create(Self);
  463. ChangeTimer.Interval := 0;
  464. ChangeTimer.Enabled := False;
  465. ChangeTimer.OnTimer := ChangeTimerOnTimer;
  466. DriveHandle := INVALID_HANDLE_VALUE;
  467. NotificationHandle := nil;
  468. end;
  469. end;
  470. procedure TDriveView.DriveRemoving(Drive: string);
  471. begin
  472. DriveRemoved(Drive);
  473. TerminateWatchThread(Drive);
  474. end;
  475. type
  476. PDevBroadcastHdr = ^TDevBroadcastHdr;
  477. TDevBroadcastHdr = record
  478. dbch_size: DWORD;
  479. dbch_devicetype: DWORD;
  480. dbch_reserved: DWORD;
  481. end;
  482. PDevBroadcastVolume = ^TDevBroadcastVolume;
  483. TDevBroadcastVolume = record
  484. dbcv_size: DWORD;
  485. dbcv_devicetype: DWORD;
  486. dbcv_reserved: DWORD;
  487. dbcv_unitmask: DWORD;
  488. dbcv_flags: WORD;
  489. end;
  490. PDEV_BROADCAST_HANDLE = ^DEV_BROADCAST_HANDLE;
  491. DEV_BROADCAST_HANDLE = record
  492. dbch_size : DWORD;
  493. dbch_devicetype : DWORD;
  494. dbch_reserved : DWORD;
  495. dbch_handle : THandle;
  496. dbch_hdevnotify : HDEVNOTIFY ;
  497. dbch_eventguid : TGUID;
  498. dbch_nameoffset : LongInt;
  499. dbch_data : Byte;
  500. end;
  501. PPItemIDList = ^PItemIDList;
  502. const
  503. DBT_DEVTYP_HANDLE = $00000006;
  504. DBT_CONFIGCHANGED = $0018;
  505. DBT_DEVICEARRIVAL = $8000;
  506. DBT_DEVICEQUERYREMOVE = $8001;
  507. DBT_DEVICEREMOVEPENDING = $8003;
  508. DBT_DEVICEREMOVECOMPLETE = $8004;
  509. DBT_DEVTYP_VOLUME = $00000002;
  510. // WORKAROUND Declaration in Winapi.ShlObj.pas is wrong
  511. function SHChangeNotification_Lock(hChange: THandle; dwProcId: DWORD;
  512. var PPidls: PPItemIDList; var plEvent: Longint): THANDLE; stdcall;
  513. external 'shell32.dll' name 'SHChangeNotification_Lock';
  514. procedure TDriveView.InternalWndProc(var Msg: TMessage);
  515. var
  516. DeviceType: DWORD;
  517. UnitMask: DWORD;
  518. DeviceHandle: THandle;
  519. Drive: Char;
  520. DriveStatusPair: TDriveStatusPair;
  521. PPIDL: PPItemIDList;
  522. Event: LONG;
  523. Lock: THandle;
  524. begin
  525. with Msg do
  526. begin
  527. if Msg = WM_USER_SHCHANGENOTIFY then
  528. begin
  529. Lock := SHChangeNotification_Lock(wParam, lParam, PPIDL, Event);
  530. try
  531. if (Event = SHCNE_RENAMEFOLDER) or // = drive rename
  532. (Event = SHCNE_MEDIAINSERTED) or // also bitlocker drive unlock (also sends SHCNE_UPDATEDIR)
  533. (Event = SHCNE_MEDIAREMOVED) then
  534. begin
  535. ScheduleDriveRefresh;
  536. end;
  537. finally
  538. SHChangeNotification_Unlock(Lock);
  539. end;
  540. end
  541. else
  542. if Msg = WM_DEVICECHANGE then
  543. begin
  544. if (wParam = DBT_CONFIGCHANGED) or
  545. (wParam = DBT_DEVICEARRIVAL) or
  546. (wParam = DBT_DEVICEREMOVECOMPLETE) then
  547. begin
  548. ScheduleDriveRefresh;
  549. end
  550. else
  551. if (wParam = DBT_DEVICEQUERYREMOVE) or
  552. (wParam = DBT_DEVICEREMOVEPENDING) then
  553. begin
  554. DeviceType := PDevBroadcastHdr(lParam)^.dbch_devicetype;
  555. // This is specifically for VeraCrypt.
  556. // For normal drives, see DBT_DEVTYP_HANDLE below
  557. // (and maybe now that we have generic implementation, this specific code for VeraCrypt might not be needed anymore)
  558. if DeviceType = DBT_DEVTYP_VOLUME then
  559. begin
  560. UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
  561. Drive := FirstDrive;
  562. while UnitMask > 0 do
  563. begin
  564. if UnitMask and $01 <> 0 then
  565. begin
  566. DriveRemoving(Drive);
  567. end;
  568. UnitMask := UnitMask shr 1;
  569. Drive := Chr(Ord(Drive) + 1);
  570. end;
  571. end
  572. else
  573. if DeviceType = DBT_DEVTYP_HANDLE then
  574. begin
  575. DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
  576. for DriveStatusPair in FDriveStatus do
  577. if DriveStatusPair.Value.DriveHandle = DeviceHandle then
  578. begin
  579. DriveRemoving(DriveStatusPair.Key);
  580. end;
  581. end;
  582. end;
  583. end
  584. else
  585. if Msg = WM_TIMER then
  586. begin
  587. CancelDriveRefresh;
  588. try
  589. //DriveInfo.Load;
  590. RefreshRootNodes(dsAll or dvdsRereadAllways);
  591. DoRefreshDrives(True);
  592. except
  593. Application.HandleException(Self);
  594. end;
  595. end;
  596. Result := DefWindowProc(FInternalWindowHandle, Msg, wParam, lParam);
  597. end;
  598. end;
  599. procedure TDriveView.DoRefreshDrives(Global: Boolean);
  600. begin
  601. if Assigned(OnRefreshDrives) then
  602. OnRefreshDrives(Self, Global);
  603. end;
  604. procedure TDriveView.CancelDriveRefresh;
  605. begin
  606. KillTimer(FInternalWindowHandle, 1);
  607. end;
  608. procedure TDriveView.ScheduleDriveRefresh;
  609. begin
  610. CancelDriveRefresh;
  611. // Delay refreshing drives for a sec.
  612. // Particularly with CD/DVD drives, if we query display name
  613. // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
  614. // Actually one sec does not help usually, but we do not want to wait any longer,
  615. // because we want to add USB drives asap.
  616. // And this problem might be solved now by SHChangeNotifyRegister/SHCNE_RENAMEFOLDER.
  617. SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
  618. end;
  619. procedure TDriveView.CreateWnd;
  620. var
  621. DriveStatus: TDriveStatus;
  622. begin
  623. inherited;
  624. if Assigned(PopupMenu) then
  625. PopupMenu.Autopopup := False;
  626. FDragDropFilesEx.SourceEffects := [deCopy, deMove, deLink];
  627. FDragDropFilesEx.TargetEffects := [deCopy, deMove, deLink];
  628. if FPrevSelectedIndex >= 0 then
  629. begin
  630. FPrevSelected := Items[FPrevSelectedIndex];
  631. FPrevSelectedIndex := -1;
  632. end;
  633. for DriveStatus in FDriveStatus.Values do
  634. with DriveStatus do
  635. begin
  636. if RootNodeIndex >= 0 then
  637. begin
  638. RootNode := Items[RootNodeIndex];
  639. RootNodeIndex := -1;
  640. end;
  641. end;
  642. end; {CreateWnd}
  643. procedure TDriveView.DestroyWnd;
  644. var
  645. DriveStatus: TDriveStatus;
  646. begin
  647. if CreateWndRestores and (Items.Count > 0) and (csRecreating in ControlState) then
  648. begin
  649. FPrevSelectedIndex := -1;
  650. if Assigned(FPrevSelected) then
  651. begin
  652. FPrevSelectedIndex := FPrevSelected.AbsoluteIndex;
  653. FPrevSelected := nil;
  654. end;
  655. for DriveStatus in FDriveStatus.Values do
  656. with DriveStatus do
  657. begin
  658. RootNodeIndex := -1;
  659. if Assigned(RootNode) then
  660. begin
  661. RootNodeIndex := RootNode.AbsoluteIndex;
  662. RootNode := nil;
  663. end;
  664. end;
  665. end;
  666. inherited;
  667. end;
  668. function TDriveView.NodeColor(Node: TTreeNode): TColor;
  669. begin
  670. Result := clDefaultItemColor;
  671. with TNodeData(Node.Data) do
  672. if not Node.Selected then
  673. begin
  674. {Colored display of compressed directories:}
  675. if (Attr and FILE_ATTRIBUTE_COMPRESSED) <> 0 then
  676. begin
  677. if SupportsDarkMode and DarkMode then Result := clSkyBlue
  678. else Result := clBlue;
  679. end
  680. else
  681. {Dimmed display, if hidden-atrribut set:}
  682. if FDimmHiddenDirs and ((Attr and FILE_ATTRIBUTE_HIDDEN) <> 0) then
  683. Result := clGrayText
  684. end;
  685. end;
  686. function TDriveView.GetCustomDirView: TCustomDirView;
  687. begin
  688. Result := DirView;
  689. end;
  690. procedure TDriveView.SetCustomDirView(Value: TCustomDirView);
  691. begin
  692. DirView := Value as TDirView;
  693. end;
  694. function TDriveView.NodePath(Node: TTreeNode): string;
  695. var
  696. ParentNode: TTreeNode;
  697. begin
  698. if not Assigned(Node) then
  699. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDirPath']));
  700. Result := GetDirName(Node);
  701. ParentNode := Node.Parent;
  702. while (ParentNode <> nil) and (ParentNode.Level >= 0) do
  703. begin
  704. if ParentNode.Level > 0 then
  705. Result := GetDirName(ParentNode) + '\' + Result
  706. else
  707. Result := GetDirName(ParentNode) + Result;
  708. ParentNode := ParentNode.Parent;
  709. end;
  710. if IsRootPath(Result) then
  711. Result := ExcludeTrailingBackslash(Result);
  712. end;
  713. {NodePathName: Returns the complete path to Node with trailing backslash on rootnodes:
  714. C:\ ,C:\WINDOWS, C:\WINDOWS\SYSTEM }
  715. function TDriveView.NodePathName(Node: TTreeNode): string;
  716. begin
  717. Result := NodePath(Node);
  718. if IsRootPath(Result) then
  719. Result := IncludeTrailingBackslash(Result);
  720. end; {NodePathName}
  721. function TDriveView.NodeIsRecycleBin(Node: TTreeNode): Boolean;
  722. begin
  723. Result := TNodeData(Node.Data).IsRecycleBin;
  724. end;
  725. function TDriveView.NodePathExists(Node: TTreeNode): Boolean;
  726. begin
  727. Result := DirectoryExists(NodePathName(Node));
  728. end;
  729. function TDriveView.CanEdit(Node: TTreeNode): Boolean;
  730. begin
  731. Result := inherited CanEdit(Node) or FForceRename;
  732. if Result then
  733. begin
  734. Result := Assigned(Node.Parent) and
  735. (not TNodeData(Node.Data).IsRecycleBin) and
  736. (not ReadOnly) and
  737. (FDragDropFilesEx.DragDetectStatus <> ddsDrag) and
  738. ((TNodeData(Node.Data).Attr and (faReadOnly or faSysFile)) = 0) and
  739. (UpperCase(Node.Text) = UpperCase(GetDirName(Node)));
  740. end;
  741. FForceRename := False;
  742. end; {CanEdit}
  743. procedure TDriveView.Edit(const Item: TTVItem);
  744. var
  745. Node: TTreeNode;
  746. Info: string;
  747. i: Integer;
  748. begin
  749. Node := GetNodeFromHItem(Item);
  750. if (Length(Item.pszText) > 0) and (Item.pszText <> Node.Text) then
  751. begin
  752. if StrContains(coInvalidDosChars, Item.pszText) then
  753. begin
  754. Info := coInvalidDosChars;
  755. for i := Length(Info) downto 1 do
  756. System.Insert(Space, Info, i);
  757. if Length(Item.pszText) > 0 then
  758. raise EInvalidDirName.Create(SErrorInvalidName + Space + Info);
  759. Exit;
  760. end;
  761. StopWatchThread;
  762. if Assigned(DirView) then
  763. DirView.StopWatchThread;
  764. with FFileOperator do
  765. begin
  766. Flags := FileOperatorDefaultFlags + [foNoConfirmation];
  767. Operation := foRename;
  768. OperandFrom.Clear;
  769. OperandTo.Clear;
  770. OperandFrom.Add(NodePath(Node));
  771. OperandTo.Add(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText);
  772. end;
  773. try
  774. if FFileOperator.Execute then
  775. begin
  776. Node.Text := Item.pszText;
  777. TNodeData(Node.Data).DirName := Item.pszText;
  778. SortChildren(Node.Parent, False);
  779. inherited;
  780. end
  781. else
  782. begin
  783. if FileOrDirExists(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText) then
  784. Info := SErrorRenameFileExists + Item.pszText
  785. else
  786. Info := SErrorRenameFile + Item.pszText;
  787. MessageBeep(MB_ICONHAND);
  788. if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then
  789. begin
  790. FLastRenameName := Item.pszText;
  791. FRenameNode := Node;
  792. PostMessage(Self.Handle, WM_USER_RENAME, 0, 0);
  793. end;
  794. end;
  795. finally
  796. StartWatchThread;
  797. if Assigned(DirView) then
  798. begin
  799. DirView.Reload2;
  800. DirView.StartWatchThread;
  801. end;
  802. end;
  803. end;
  804. end; {Edit}
  805. procedure TDriveView.WMUserRename(var Message: TMessage);
  806. begin
  807. if Assigned(FRenameNode) then
  808. begin
  809. FForceRename := True;
  810. TreeView_EditLabel(Handle, FRenameNode.ItemID);
  811. SetWindowText(TreeView_GetEditControl(Self.Handle), PChar(FLastRenameName));
  812. FRenameNode := nil;
  813. end;
  814. end; {WMUserRename}
  815. function TDriveView.CanExpand(Node: TTreeNode): Boolean;
  816. var
  817. SubNode: TTreeNode;
  818. Drive: string;
  819. SaveCursor: TCursor;
  820. begin
  821. Result := inherited CanExpand(Node);
  822. Drive := GetDriveToNode(Node);
  823. if Node.HasChildren then
  824. begin
  825. if (Node.Level = 0) and
  826. (not GetDriveStatus(Drive).Scanned) and
  827. DriveInfo.IsFixedDrive(Drive) then
  828. begin
  829. SubNode := Node.GetFirstChild;
  830. if not Assigned(SubNode) then
  831. begin
  832. ScanDrive(Drive);
  833. SubNode := Node.GetFirstChild;
  834. Node.HasChildren := Assigned(SubNode);
  835. Result := Node.HasChildren;
  836. if not Assigned(GetDriveStatus(Drive).DiscMonitor) then
  837. CreateWatchThread(Drive);
  838. end;
  839. end
  840. else
  841. begin
  842. SaveCursor := Screen.Cursor;
  843. Screen.Cursor := crHourGlass;
  844. try
  845. if (not TNodeData(Node.Data).Scanned) and DoScanDir(Node) then
  846. begin
  847. ReadSubDirs(Node, DriveInfo.Get(Drive).DriveType);
  848. end;
  849. finally
  850. Screen.Cursor := SaveCursor;
  851. end;
  852. end;
  853. end;
  854. end; {CanExpand}
  855. procedure TDriveView.GetImageIndex(Node: TTreeNode);
  856. begin
  857. if TNodeData(Node.Data).IconEmpty then
  858. SetImageIndex(Node);
  859. inherited;
  860. end; {GetImageIndex}
  861. procedure TDriveView.Loaded;
  862. begin
  863. inherited;
  864. {Create the drive nodes:}
  865. RefreshRootNodes(dsDisplayName or dvdsFloppy);
  866. {Set the initial directory:}
  867. if (Length(FDirectory) > 0) and DirectoryExists(FDirectory) then
  868. Directory := FDirectory;
  869. FCreating := False;
  870. end; {Loaded}
  871. function TDriveView.CreateNode: TTreeNode;
  872. begin
  873. Result := TDriveTreeNode.Create(Items);
  874. end;
  875. procedure TDriveView.Delete(Node: TTreeNode);
  876. var
  877. NodeData: TNodeData;
  878. begin
  879. if Node = FPrevSelected then
  880. FPrevSelected := nil;
  881. NodeData := nil;
  882. if Assigned(Node) and Assigned(Node.Data) then
  883. NodeData := TNodeData(Node.Data);
  884. Node.Data := nil;
  885. inherited;
  886. if Assigned(NodeData) and not (csRecreating in ControlState) then
  887. begin
  888. NodeData.Destroy;
  889. end;
  890. end; {OnDelete}
  891. procedure TDriveView.KeyPress(var Key: Char);
  892. begin
  893. inherited;
  894. if Assigned(Selected) then
  895. begin
  896. if Pos(Key, coInvalidDosChars) <> 0 then
  897. begin
  898. Beep;
  899. Key := #0;
  900. end;
  901. end;
  902. end; {KeyPress}
  903. function TDriveView.CanChange(Node: TTreeNode): Boolean;
  904. var
  905. Path: string;
  906. Drive: string;
  907. begin
  908. Result := inherited CanChange(Node);
  909. if not Reading and not (csRecreating in ControlState) then
  910. begin
  911. if Result and Assigned(Node) then
  912. begin
  913. Path := NodePathName(Node);
  914. if Path <> FLastDir then
  915. begin
  916. Drive := DriveInfo.GetDriveKey(Path);
  917. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  918. if not DriveInfo.Get(Drive).DriveReady then
  919. begin
  920. MessageDlg(Format(SDriveNotReady, [Drive]), mtError, [mbOK], 0);
  921. Result := False;
  922. end
  923. else
  924. try
  925. CheckCanOpenDirectory(Path);
  926. except
  927. Application.HandleException(Self);
  928. Result := False;
  929. end;
  930. end;
  931. end;
  932. if Result and (csDestroying in ComponentState) then
  933. begin
  934. Result := False;
  935. end;
  936. if Result and
  937. (not FCanChange) and
  938. Assigned(Node) and
  939. Assigned(Node.Data) and
  940. Assigned(Selected) and
  941. Assigned(Selected.Data) then
  942. begin
  943. DropTarget := Node;
  944. Result := False;
  945. end
  946. else
  947. begin
  948. DropTarget := nil;
  949. end;
  950. end;
  951. end; {CanChange}
  952. procedure TDriveView.Change(Node: TTreeNode);
  953. var
  954. Drive: string;
  955. OldSerial: DWORD;
  956. NewDir: string;
  957. PrevDrive: string;
  958. begin
  959. if not Reading and not (csRecreating in ControlState) then
  960. begin
  961. if Assigned(Node) then
  962. begin
  963. NewDir := NodePathName(Node);
  964. if NewDir <> FLastDir then
  965. begin
  966. Drive := DriveInfo.GetDriveKey(NewDir);
  967. if Length(FLastDir) > 0 then
  968. PrevDrive := DriveInfo.GetDriveKey(FLastDir)
  969. else
  970. PrevDrive := '';
  971. FChangeFlag := True;
  972. FLastDir := NewDir;
  973. OldSerial := DriveInfo.Get(Drive).DriveSerial;
  974. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  975. with DriveInfo.Get(Drive) do
  976. begin
  977. if Assigned(FDirView) and (FDirView.Path <> NewDir) then
  978. FDirView.Path := NewDir;
  979. if DriveReady then
  980. begin
  981. if not DirectoryExists(NewDir) then
  982. begin
  983. ValidateDirectory(GetDriveStatus(Drive).RootNode);
  984. Exit;
  985. end;
  986. GetDriveStatus(Drive).DefaultDir := IncludeTrailingBackslash(NewDir);
  987. if PrevDrive <> Drive then
  988. begin
  989. if (PrevDrive <> '') and
  990. (DriveInfo.Get(PrevDrive).DriveType = DRIVE_REMOVABLE) then
  991. begin
  992. TerminateWatchThread(PrevDrive);
  993. end;
  994. {Drive serial has changed or is missing: allways reread the drive:}
  995. if (DriveSerial <> OldSerial) or (DriveSerial = 0) then
  996. begin
  997. if TNodeData(GetDriveStatus(Drive).RootNode.Data).Scanned then
  998. ScanDrive(Drive);
  999. end;
  1000. end;
  1001. StartWatchThread;
  1002. end
  1003. else {Drive not ready:}
  1004. begin
  1005. GetDriveStatus(Drive).RootNode.DeleteChildren;
  1006. GetDriveStatus(Drive).DefaultDir := EmptyStr;
  1007. end;
  1008. end;
  1009. end;
  1010. if (not Assigned(FPrevSelected)) or (not FPrevSelected.HasAsParent(Node)) then
  1011. Node.Expand(False);
  1012. FPrevSelected := Node;
  1013. ValidateCurrentDirectoryIfNotMonitoring;
  1014. end;
  1015. end;
  1016. inherited;
  1017. end; {Change}
  1018. procedure TDriveView.SetImageIndex(Node: TTreeNode);
  1019. var
  1020. FileInfo: TShFileInfo;
  1021. Drive, NodePath: string;
  1022. begin
  1023. if Assigned(Node) and TNodeData(Node.Data).IconEmpty then
  1024. begin
  1025. NodePath := NodePathName(Node);
  1026. Drive := DriveInfo.GetDriveKey(NodePath);
  1027. if Node.Level = 0 then
  1028. begin
  1029. with DriveInfo.Get(Drive) do
  1030. begin
  1031. if ImageIndex = 0 then
  1032. begin
  1033. DriveInfo.ReadDriveStatus(Drive, dsImageIndex);
  1034. Node.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1035. end
  1036. else Node.ImageIndex := ImageIndex;
  1037. Node.SelectedIndex := Node.ImageIndex;
  1038. end;
  1039. end
  1040. else
  1041. begin
  1042. if DriveInfo.Get(Drive).DriveType = DRIVE_REMOTE then
  1043. begin
  1044. Node.ImageIndex := StdDirIcon;
  1045. Node.SelectedIndex := StdDirSelIcon;
  1046. end
  1047. else
  1048. begin
  1049. try
  1050. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1051. SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  1052. if (FileInfo.iIcon < Images.Count) and (FileInfo.iIcon > 0) then
  1053. begin
  1054. Node.ImageIndex := FileInfo.iIcon;
  1055. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1056. SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_OPENICON);
  1057. Node.SelectedIndex := FileInfo.iIcon;
  1058. end
  1059. else
  1060. begin
  1061. Node.ImageIndex := StdDirIcon;
  1062. Node.SelectedIndex := StdDirSelIcon;
  1063. end;
  1064. except
  1065. Node.ImageIndex := StdDirIcon;
  1066. Node.SelectedIndex := StdDirSelIcon;
  1067. end;
  1068. end;
  1069. end;
  1070. end; {IconEmpty}
  1071. TNodeData(Node.Data).IconEmpty := False;
  1072. end; {SetImageIndex}
  1073. function TDriveView.GetDriveText(Drive: string): string;
  1074. begin
  1075. if FShowVolLabel and (Length(DriveInfo.GetPrettyName(Drive)) > 0) then
  1076. begin
  1077. case FVolDisplayStyle of
  1078. doPrettyName: Result := DriveInfo.GetPrettyName(Drive);
  1079. doDisplayName: Result := DriveInfo.GetDisplayName(Drive);
  1080. end; {Case}
  1081. end
  1082. else
  1083. begin
  1084. Result := DriveInfo.GetSimpleName(Drive);
  1085. end;
  1086. end; {GetDriveText}
  1087. function CompareDrive(List: TStringList; Index1, Index2: Integer): Integer;
  1088. var
  1089. Drive1, Drive2: string;
  1090. RealDrive1, RealDrive2: Boolean;
  1091. begin
  1092. Drive1 := List[Index1];
  1093. Drive2 := List[Index2];
  1094. RealDrive1 := DriveInfo.IsRealDrive(Drive1);
  1095. RealDrive2 := DriveInfo.IsRealDrive(Drive2);
  1096. if RealDrive1 = RealDrive2 then
  1097. begin
  1098. Result := CompareText(Drive1, Drive2);
  1099. end
  1100. else
  1101. if RealDrive1 and (not RealDrive2) then
  1102. begin
  1103. Result := -1;
  1104. end
  1105. else
  1106. begin
  1107. Result := 1;
  1108. end;
  1109. end;
  1110. function TDriveView.GetDrives: TStrings;
  1111. var
  1112. DriveStatusPair: TDriveStatusPair;
  1113. Drives: TStringList;
  1114. begin
  1115. Drives := TStringList.Create;
  1116. { We could iterate only .Keys here, but that crashes IDE for some reason }
  1117. for DriveStatusPair in FDriveStatus do
  1118. begin
  1119. Drives.Add(DriveStatusPair.Key);
  1120. end;
  1121. Drives.CustomSort(CompareDrive);
  1122. Result := Drives;
  1123. end;
  1124. procedure TDriveView.DriveRemoved(Drive: string);
  1125. var
  1126. NewDrive: Char;
  1127. begin
  1128. if (Directory <> '') and (Directory[1] = Drive) then
  1129. begin
  1130. if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
  1131. else NewDrive := SystemDrive;
  1132. repeat
  1133. if NewDrive < SystemDrive then NewDrive := SystemDrive
  1134. else
  1135. if NewDrive = SystemDrive then NewDrive := LastDrive
  1136. else Dec(NewDrive);
  1137. DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
  1138. if NewDrive = Drive then
  1139. begin
  1140. Break;
  1141. end;
  1142. if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
  1143. begin
  1144. Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
  1145. break;
  1146. end;
  1147. until False;
  1148. if not Assigned(Selected) then
  1149. begin
  1150. Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
  1151. end;
  1152. end;
  1153. end;
  1154. procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
  1155. var
  1156. Drives: TStrings;
  1157. NewText: string;
  1158. SaveCursor: TCursor;
  1159. WasValid: Boolean;
  1160. NodeData: TNodeData;
  1161. DriveStatus: TDriveStatus;
  1162. NextDriveNode: TTreeNode;
  1163. Index: Integer;
  1164. Drive: string;
  1165. begin
  1166. SaveCursor := Screen.Cursor;
  1167. Screen.Cursor := crHourGlass;
  1168. Drives := nil;
  1169. try
  1170. Drives := GetDrives;
  1171. NextDriveNode := nil;
  1172. for Index := Drives.Count - 1 downto 0 do
  1173. begin
  1174. Drive := Drives[Index];
  1175. DriveStatus := GetDriveStatus(Drive);
  1176. if ((dsFlags and dvdsFloppy) <> 0) or DriveInfo.IsFixedDrive(Drive) then
  1177. begin
  1178. with DriveInfo.Get(Drive) do
  1179. begin
  1180. WasValid := Assigned(DriveStatus.RootNode);
  1181. end;
  1182. if ((dsFlags and dvdsReReadAllways) = 0) and
  1183. (Length(DriveInfo.Get(Drive).DisplayName) > 0) then
  1184. dsFlags := dsFlags and (not dsDisplayName);
  1185. DriveInfo.ReadDriveStatus(Drive, dsFlags);
  1186. with DriveInfo.Get(Drive), DriveStatus do
  1187. begin
  1188. if Valid then
  1189. begin
  1190. if not WasValid then
  1191. {New drive has arrived: insert new rootnode:}
  1192. begin
  1193. { Create root directory node }
  1194. NodeData := TNodeData.Create;
  1195. NodeData.DirName := DriveInfo.GetDriveRoot(Drive);
  1196. if Assigned(NextDriveNode) then
  1197. RootNode := Items.InsertObject(NextDriveNode, '', NodeData)
  1198. else
  1199. RootNode := Items.AddObject(nil, '', NodeData);
  1200. RootNode.Text := GetDisplayName(RootNode);
  1201. RootNode.HasChildren := True;
  1202. Scanned := False;
  1203. Verified := False;
  1204. end
  1205. else
  1206. if RootNode.ImageIndex <> DriveInfo.Get(Drive).ImageIndex then
  1207. begin {WasValid = True}
  1208. RootNode.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1209. RootNode.SelectedIndex := DriveInfo.Get(Drive).ImageIndex;
  1210. end;
  1211. if Assigned(RootNode) then
  1212. begin
  1213. NewText := GetDisplayName(RootNode);
  1214. if RootNode.Text <> NewText then
  1215. RootNode.Text := NewText;
  1216. end;
  1217. end
  1218. else
  1219. if WasValid then
  1220. {Drive has been removed => delete rootnode:}
  1221. begin
  1222. DriveRemoved(Drive);
  1223. Scanned := False;
  1224. Verified := False;
  1225. RootNode.Delete;
  1226. RootNode := nil;
  1227. end;
  1228. end;
  1229. end;
  1230. if Assigned(DriveStatus.RootNode) then
  1231. NextDriveNode := DriveStatus.RootNode;
  1232. end;
  1233. finally
  1234. Screen.Cursor := SaveCursor;
  1235. Drives.Free;
  1236. end;
  1237. end; {RefreshRootNodes}
  1238. function TDriveView.AddChildNode(ParentNode: TTreeNode; ParentPath: string; SRec: TSearchRec): TTreeNode;
  1239. var
  1240. NewNode: TTreeNode;
  1241. NodeData: TNodeData;
  1242. SubSRec: TSearchRec;
  1243. begin
  1244. NodeData := TNodeData.Create;
  1245. NodeData.Attr := SRec.Attr;
  1246. NodeData.DirName := SRec.Name;
  1247. NodeData.FIsRecycleBin :=
  1248. (SRec.Attr and faSysFile <> 0) and
  1249. (ParentNode.Level = 0) and
  1250. (SameText(SRec.Name, 'RECYCLED') or
  1251. SameText(SRec.Name, 'RECYCLER') or
  1252. SameText(SRec.Name, '$RECYCLE.BIN'));
  1253. NewNode := Self.Items.AddChildObject(ParentNode, '', NodeData);
  1254. NewNode.Text := GetDisplayName(NewNode);
  1255. // query content ("has subfolder") only if tree view is visible
  1256. // to avoid unnecessary scan of subfolders (which may take some time)
  1257. // if tree view is not visible anyway
  1258. if Visible and
  1259. Assigned(Parent) and
  1260. // Ad-hoc test for "other/right" panel, which is not hidden directly, but indirectly by hiding it container panel
  1261. ((Parent is TCustomForm) or Parent.Visible) and
  1262. (GetDriveTypeToNode(ParentNode) <> DRIVE_REMOTE) then
  1263. begin
  1264. NewNode.HasChildren := FindFirstSubDir(IncludeTrailingBackslash(ParentPath) + NodeData.DirName + '\*.*', SubSRec);
  1265. FindClose(SubSRec);
  1266. end
  1267. else
  1268. begin
  1269. NewNode.HasChildren := True;
  1270. end;
  1271. NodeData.Scanned := not NewNode.HasChildren;
  1272. Result := NewNode;
  1273. end; {AddChildNode}
  1274. function TDriveView.GetDriveStatus(Drive: string): TDriveStatus;
  1275. begin
  1276. if not FDriveStatus.TryGetValue(Drive, Result) then
  1277. begin
  1278. Result := CreateDriveStatus;
  1279. FDriveStatus.Add(Drive, Result);
  1280. RefreshRootNodes(dsAll or dvdsRereadAllways);
  1281. DoRefreshDrives(False);
  1282. end;
  1283. end; {GetDriveStatus}
  1284. function TDriveView.DoScanDir(FromNode: TTreeNode): Boolean;
  1285. begin
  1286. Result := not TNodeData(FromNode.Data).IsRecycleBin;
  1287. end; {DoScanDir}
  1288. function TDriveView.DirAttrMask: Integer;
  1289. begin
  1290. Result := faDirectory or faSysFile;
  1291. if ShowHiddenDirs then
  1292. Result := Result or faHidden;
  1293. end;
  1294. procedure TDriveView.ScanDrive(Drive: string);
  1295. begin {ScanDrive}
  1296. with Self.Items do
  1297. begin
  1298. ValidateDirectory(FindNodeToPath(DriveInfo.GetDriveRoot(Drive)));
  1299. GetDriveStatus(Drive).Scanned := True;
  1300. GetDriveStatus(Drive).Verified := False;
  1301. end;
  1302. end; {ScanDrive}
  1303. function TDriveView.DoFindNodeToPath(Path: string; ExistingOnly: Boolean): TTreeNode;
  1304. function SearchSubDirs(ParentNode: TTreeNode; Path: string): TTreeNode; forward;
  1305. function ExtractFirstName(S: string): string;
  1306. var
  1307. I: Integer;
  1308. begin
  1309. I := Pos('\', S);
  1310. if I = 0 then
  1311. I := Length(S);
  1312. Result := System.Copy(S, 1, I);
  1313. end;
  1314. function DoSearchSubDirs(ParentNode: TTreeNode; Path: string): TTreeNode;
  1315. var
  1316. Node: TTreeNode;
  1317. Dir: string;
  1318. begin
  1319. {Extract first directory from path:}
  1320. Dir := ExtractFirstName(Path);
  1321. System.Delete(Path, 1, Length(Dir));
  1322. if Dir[Length(Dir)] = '\' then
  1323. SetLength(Dir, Pred(Length(Dir)));
  1324. Node := ParentNode.GetFirstChild;
  1325. if (not Assigned(Node)) and (not ExistingOnly) then
  1326. begin
  1327. ValidateDirectoryEx(ParentNode, rsRecursiveExisting, True);
  1328. Node := ParentNode.GetFirstChild;
  1329. end;
  1330. Result := nil;
  1331. while Assigned(Node) do
  1332. begin
  1333. if UpperCase(GetDirName(Node)) = Dir then
  1334. begin
  1335. if Length(Path) > 0 then
  1336. begin
  1337. Result := SearchSubDirs(Node, Path)
  1338. end
  1339. else
  1340. begin
  1341. Result := Node;
  1342. end;
  1343. Exit;
  1344. end;
  1345. Node := ParentNode.GetNextChild(Node);
  1346. end;
  1347. end;
  1348. function SearchSubDirs(ParentNode: TTreeNode; Path: string): TTreeNode;
  1349. begin
  1350. Result := nil;
  1351. if Length(Path) > 0 then
  1352. begin
  1353. if (not TNodeData(ParentNode.Data).Scanned) and (not ExistingOnly) then
  1354. begin
  1355. ReadSubDirs(ParentNode, GetDriveTypeToNode(ParentNode));
  1356. end;
  1357. // Factored out of DoSearchSubDirs is remnant of Bug 956 superceded by Bug 1320
  1358. Result := DoSearchSubDirs(ParentNode, Path);
  1359. if (not Assigned(Result)) and
  1360. DirectoryExists(IncludeTrailingBackslash(NodePath(ParentNode)) + Path) and
  1361. (not ExistingOnly) then
  1362. begin
  1363. ReadSubDirs(ParentNode, GetDriveTypeToNode(ParentNode), ExcludeTrailingBackslash(ExtractFirstName(Path)));
  1364. Result := DoSearchSubDirs(ParentNode, Path);
  1365. end;
  1366. end;
  1367. end; {SearchSubDirs}
  1368. var
  1369. Drive: string;
  1370. P: Integer;
  1371. begin {FindNodeToPath}
  1372. Result := nil;
  1373. if Length(Path) < 3 then
  1374. Exit;
  1375. // Particularly when used by TDirView to delegate browsing to
  1376. // hidden drive view, the handle may not be created
  1377. HandleNeeded;
  1378. Drive := DriveInfo.GetDriveKey(Path);
  1379. if (not Assigned(GetDriveStatus(Drive).RootNode)) and
  1380. // hidden or possibly recently un-hidden by other drive view (refresh is pending)
  1381. (DriveInfo.Get(Drive).Valid or DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy) then
  1382. begin
  1383. if DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy then
  1384. DriveInfo.OverrideDrivePolicy(Drive);
  1385. if DriveInfo.Get(Drive).Valid then
  1386. begin
  1387. CancelDriveRefresh; // cancel a possible pending refresh (see the previous comment)
  1388. RefreshRootNodes(dsAll or dvdsRereadAllways); // overkill and is likely already called by GetDriveStatus
  1389. DoRefreshDrives(False);
  1390. end;
  1391. end;
  1392. if Assigned(GetDriveStatus(Drive).RootNode) then
  1393. begin
  1394. if DriveInfo.IsRealDrive(Drive) then
  1395. begin
  1396. System.Delete(Path, 1, 3);
  1397. end
  1398. else
  1399. if IsUncPath(Path) then
  1400. begin
  1401. System.Delete(Path, 1, 2);
  1402. P := Pos('\', Path);
  1403. if P = 0 then
  1404. begin
  1405. Path := '';
  1406. end
  1407. else
  1408. begin
  1409. System.Delete(Path, 1, P);
  1410. P := Pos('\', Path);
  1411. if P = 0 then
  1412. begin
  1413. Path := '';
  1414. end
  1415. else
  1416. begin
  1417. System.Delete(Path, 1, P);
  1418. end;
  1419. end;
  1420. end
  1421. else
  1422. begin
  1423. raise EConvertError.Create(Format(ErrorInvalidDrive, [Path]))
  1424. end;
  1425. if Length(Path) > 0 then
  1426. begin
  1427. if (not GetDriveStatus(Drive).Scanned) and (not ExistingOnly) then
  1428. begin
  1429. ScanDrive(Drive);
  1430. end;
  1431. Result := SearchSubDirs(GetDriveStatus(Drive).RootNode, UpperCase(Path));
  1432. end
  1433. else Result := GetDriveStatus(Drive).RootNode;
  1434. end;
  1435. end; {FindNodetoPath}
  1436. function TDriveView.FindNodeToPath(Path: string): TTreeNode;
  1437. begin
  1438. Result := DoFindNodeToPath(Path, False);
  1439. end;
  1440. function TDriveView.TryFindNodeToPath(Path: string): TTreeNode;
  1441. begin
  1442. Result := DoFindNodeToPath(Path, True);
  1443. end;
  1444. function TDriveView.GetSubDir(var SRec: TSearchRec): Boolean;
  1445. begin
  1446. Result := True;
  1447. while Result and
  1448. ((SRec.Name = '.' ) or
  1449. (SRec.Name = '..') or
  1450. ((SRec.Attr and faDirectory) = 0)) do
  1451. begin
  1452. if FindNext(SRec) <> 0 then
  1453. begin
  1454. Result := False;
  1455. end;
  1456. end;
  1457. end;
  1458. function TDriveView.FindFirstSubDir(Path: string; var SRec: TSearchRec): Boolean;
  1459. begin
  1460. Result := (FindFirstEx(ApiPath(Path), DirAttrMask, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS, FindExSearchLimitToDirectories) = 0);
  1461. if Result then
  1462. begin
  1463. Result := GetSubDir(SRec);
  1464. // For consistency with FindFirst, but not really needed, as all callers call FindClose unconditionally anyway
  1465. if not Result then FindClose(SRec);
  1466. end;
  1467. end;
  1468. function TDriveView.FindNextSubDir(var SRec: TSearchRec): Boolean;
  1469. begin
  1470. Result := (FindNext(SRec) = 0) and GetSubDir(SRec);
  1471. end;
  1472. function TDriveView.ReadSubDirs(Node: TTreeNode; DriveType: Integer; SpecificFile: string): Boolean;
  1473. var
  1474. C: Integer;
  1475. SRec: TSearchRec;
  1476. Path: string;
  1477. Start: TDateTime;
  1478. R, All, Stop: Boolean;
  1479. begin
  1480. Result := False;
  1481. Path := NodePath(Node);
  1482. All := (SpecificFile = '');
  1483. if All then SpecificFile := '*.*';
  1484. R := FindFirstSubDir(IncludeTrailingBackslash(Path) + SpecificFile, SRec);
  1485. Start := Now;
  1486. C := 0;
  1487. // At least from SetDirectory > DoFindNodeToPath and CanExpand, this is not called within BeginUpdate/EndUpdate block.
  1488. // But in any case, adding it here makes expanding (which calls CanExpand) noticeably slower, when there are lot of nodes,
  1489. // because EndUpdate triggers TVN_GETDISPINFO for all nodes in the tree.
  1490. while R do
  1491. begin
  1492. AddChildNode(Node, Path, SRec);
  1493. Inc(C);
  1494. // There are two other directory reading loops, where this is not called
  1495. if ((C mod 100) = 0) and Assigned(OnContinueLoading) then
  1496. begin
  1497. Stop := False;
  1498. OnContinueLoading(Self, Start, Path, C, Stop);
  1499. if Stop then R := False;
  1500. end;
  1501. Result := True;
  1502. if R then
  1503. begin
  1504. R := FindNextSubDir(SRec);
  1505. end;
  1506. end;
  1507. FindClose(Srec);
  1508. if All then TNodeData(Node.Data).Scanned := True;
  1509. if Result then SortChildren(Node, False)
  1510. else
  1511. if All then Node.HasChildren := False;
  1512. Application.ProcessMessages;
  1513. end; {ReadSubDirs}
  1514. procedure TDriveView.DeleteNode(Node: TTreeNode);
  1515. var
  1516. ValidNode: TTreeNode;
  1517. begin
  1518. if Assigned(Selected) and Assigned(Node.Parent) and
  1519. ((Selected = Node) or Selected.HasAsParent(Node)) then
  1520. begin
  1521. ValidNode := Node.Parent;
  1522. while (not DirectoryExists(NodePathName(ValidNode))) and Assigned(ValidNode.Parent) do
  1523. ValidNode := ValidNode.Parent;
  1524. Selected := ValidNode;
  1525. end;
  1526. if DropTarget = Node then
  1527. DropTarget := nil;
  1528. Node.Delete;
  1529. end;
  1530. function TDriveView.CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  1531. var
  1532. WorkNode: TTreeNode;
  1533. DelNode: TTreeNode;
  1534. SRec: TSearchRec;
  1535. SrecList: TStringList;
  1536. SubDirList: TStringList;
  1537. R: Boolean;
  1538. Index: Integer;
  1539. NewDirFound: Boolean;
  1540. ParentDir: string;
  1541. NodeData: TNodeData;
  1542. ScanDirInfo: PScanDirInfo;
  1543. begin {CallBackValidateDir}
  1544. Result := True;
  1545. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  1546. Exit;
  1547. NewDirFound := False;
  1548. ScanDirInfo := PScanDirInfo(Data);
  1549. {Check, if directory still exists: (but not with root directory) }
  1550. if Assigned(Node.Parent) and (ScanDirInfo^.StartNode = Node) then
  1551. if not DirectoryExists(NodePathName(Node)) then
  1552. begin
  1553. DeleteNode(Node);
  1554. Node := nil;
  1555. Exit;
  1556. end;
  1557. WorkNode := Node.GetFirstChild;
  1558. NodeData := TNodeData(Node.Data);
  1559. if NodeData.Scanned and Assigned(WorkNode) then
  1560. {if node was already scanned: check wether the existing subnodes are still alive
  1561. and add all new subdirectories as subnodes:}
  1562. begin
  1563. if DoScanDir(Node) then
  1564. begin
  1565. ParentDir := IncludeTrailingBackslash(NodePath(Node));
  1566. {Build list of existing subnodes:}
  1567. SubDirList := TStringList.Create;
  1568. SubDirList.CaseSensitive := True; // We want to reflect changes in subfolder name case
  1569. while Assigned(WorkNode) do
  1570. begin
  1571. SubDirList.Add(TNodeData(WorkNode.Data).DirName);
  1572. WorkNode := Node.GetNextChild(WorkNode);
  1573. end;
  1574. // Nodes are sorted using natural sorting, while TStringList.Find uses simple sorting
  1575. SubDirList.Sort;
  1576. SRecList := TStringList.Create;
  1577. SRecList.CaseSensitive := True;
  1578. R := FindFirstSubDir(ParentDir + '*.*', SRec);
  1579. while R do
  1580. begin
  1581. SrecList.Add(Srec.Name);
  1582. if not SubDirList.Find(Srec.Name, Index) then
  1583. {Subnode does not exists: add it:}
  1584. begin
  1585. AddChildNode(Node, ParentDir, SRec);
  1586. NewDirFound := True;
  1587. end;
  1588. R := FindNextSubDir(Srec);
  1589. end;
  1590. FindClose(Srec);
  1591. Sreclist.Sort;
  1592. {Remove not existing subnodes:}
  1593. WorkNode := Node.GetFirstChild;
  1594. while Assigned(WorkNode) do
  1595. begin
  1596. if not Assigned(WorkNode.Data) or
  1597. not SrecList.Find(TNodeData(WorkNode.Data).DirName, Index) then
  1598. begin
  1599. DelNode := WorkNode;
  1600. WorkNode := Node.GetNextChild(WorkNode);
  1601. DeleteNode(DelNode);
  1602. end
  1603. else
  1604. begin
  1605. if (SrecList[Index] <> TNodeData(WorkNode.Data).DirName) then
  1606. begin
  1607. {Case of directory letters has changed:}
  1608. TNodeData(WorkNode.Data).DirName := SrecList[Index];
  1609. WorkNode.Text := SrecList[Index];
  1610. end;
  1611. WorkNode := Node.GetNextChild(WorkNode);
  1612. end;
  1613. end;
  1614. SrecList.Free;
  1615. SubDirList.Free;
  1616. {Sort subnodes:}
  1617. if NewDirFound then
  1618. SortChildren(Node, False);
  1619. end;
  1620. end
  1621. else
  1622. {Node was not already scanned:}
  1623. if (ScanDirInfo^.SearchNewDirs or
  1624. NodeData.Scanned or
  1625. (Node = ScanDirInfo^.StartNode)) and
  1626. DoScanDir(Node) then
  1627. begin
  1628. ReadSubDirs(Node, ScanDirInfo^.DriveType);
  1629. end;
  1630. end; {CallBackValidateDir}
  1631. procedure TDriveView.RebuildTree;
  1632. var
  1633. Drive: string;
  1634. begin
  1635. for Drive in FDriveStatus.Keys do
  1636. with GetDriveStatus(Drive) do
  1637. if Assigned(RootNode) and Scanned then
  1638. ValidateDirectory(RootNode);
  1639. end;
  1640. procedure TDriveView.ValidateCurrentDirectoryIfNotMonitoring;
  1641. begin
  1642. if Assigned(Selected) and
  1643. not Assigned(GetDriveStatus(GetDriveToNode(Selected)).DiscMonitor) then
  1644. begin
  1645. ValidateDirectory(Selected);
  1646. end;
  1647. end;
  1648. procedure TDriveView.ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  1649. NewDirs: Boolean);
  1650. var
  1651. Info: PScanDirInfo;
  1652. SelDir: string;
  1653. SaveCursor: TCursor;
  1654. RestartWatchThread: Boolean;
  1655. SaveCanChange: Boolean;
  1656. CurrentPath: string;
  1657. Drive: string;
  1658. begin
  1659. if Assigned(Node) and Assigned(Node.Data) and
  1660. (not FValidateFlag) and DoScanDir(Node) then
  1661. begin
  1662. SelDir := Directory;
  1663. SaveCursor := Screen.Cursor;
  1664. if Self.Focused and (Screen.Cursor <> crHourGlass) then
  1665. Screen.Cursor := crHourGlass;
  1666. CurrentPath := NodePath(Node);
  1667. Drive := DriveInfo.GetDriveKey(CurrentPath);
  1668. if Node.Level = 0 then
  1669. GetDriveStatus(Drive).ChangeTimer.Enabled := False;
  1670. RestartWatchThread := WatchThreadActive;
  1671. try
  1672. if WatchThreadActive then
  1673. StopWatchThread;
  1674. FValidateFlag := True;
  1675. New(Info);
  1676. Info^.StartNode := Node;
  1677. Info^.SearchNewDirs := NewDirs;
  1678. Info^.DriveType := DriveInfo.Get(Drive).DriveType;
  1679. SaveCanChange := FCanChange;
  1680. FCanChange := True;
  1681. FChangeFlag := False;
  1682. Items.BeginUpdate;
  1683. try
  1684. IterateSubTree(Node, CallBackValidateDir, Recurse, coScanStartNode, Info);
  1685. finally
  1686. Items.EndUpdate;
  1687. end;
  1688. FValidateFlag := False;
  1689. if (not Assigned(Selected)) and (Length(SelDir) > 0) then
  1690. Directory := ExtractFileDrive(SelDir);
  1691. if (SelDir <> Directory) and (not FChangeFlag) then
  1692. Change(Selected);
  1693. FCanChange := SaveCanChange;
  1694. Dispose(Info);
  1695. finally
  1696. if RestartWatchThread and FWatchDirectory and not WatchThreadActive then
  1697. StartWatchThread;
  1698. if Screen.Cursor <> SaveCursor then
  1699. Screen.Cursor := SaveCursor;
  1700. end;
  1701. end;
  1702. end; {ValidateDirectoryEx}
  1703. function TDriveView.GetDriveTypeToNode(Node: TTreeNode): Integer;
  1704. begin
  1705. Assert(Assigned(Node));
  1706. Result := DriveInfo.Get(GetDriveToNode(Node)).DriveType;
  1707. end; {GetDriveTypeToNode}
  1708. procedure TDriveView.CreateWatchThread(Drive: string);
  1709. begin
  1710. if csDesigning in ComponentState then
  1711. Exit;
  1712. if (not Assigned(GetDriveStatus(Drive).DiscMonitor)) and
  1713. FWatchDirectory and
  1714. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) then
  1715. begin
  1716. with GetDriveStatus(Drive) do
  1717. begin
  1718. DiscMonitor := TDiscMonitor.Create(Self);
  1719. DiscMonitor.ChangeDelay := msThreadChangeDelay;
  1720. DiscMonitor.SubTree := True;
  1721. DiscMonitor.Filters := [moDirName];
  1722. DiscMonitor.OnChange := ChangeDetected;
  1723. DiscMonitor.OnInvalid := ChangeInvalid;
  1724. DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
  1725. DiscMonitor.Open;
  1726. end;
  1727. UpdateDriveNotifications(Drive);
  1728. end;
  1729. end; {CreateWatchThread}
  1730. procedure TDriveView.SetWatchDirectory(Value: Boolean);
  1731. begin
  1732. if FWatchDirectory <> Value then
  1733. begin
  1734. FWatchDirectory := Value;
  1735. if (not (csDesigning in ComponentState)) and Value then
  1736. StartAllWatchThreads
  1737. else
  1738. StopAllWatchThreads;
  1739. end;
  1740. end; {SetAutoScan}
  1741. procedure TDriveView.SetDirView(Value: TDirView);
  1742. begin
  1743. if Assigned(FDirView) then
  1744. FDirView.DriveView := nil;
  1745. FDirView := Value;
  1746. if Assigned(FDirView) then
  1747. FDirView.DriveView := Self;
  1748. end; {SetDirView}
  1749. function TDriveView.NodeWatched(Node: TTreeNode): Boolean;
  1750. var
  1751. Drive: string;
  1752. begin
  1753. Drive := GetDriveToNode(Node);
  1754. Result := WatchThreadActive(Drive);
  1755. end; {NodeWatched}
  1756. procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
  1757. const ErrorStr: string);
  1758. var
  1759. Drive: string;
  1760. begin
  1761. Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
  1762. with GetDriveStatus(Drive) do
  1763. begin
  1764. DiscMonitor.Close;
  1765. end;
  1766. UpdateDriveNotifications(Drive);
  1767. end; {DirWatchChangeInvalid}
  1768. procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
  1769. var SubdirsChanged: Boolean);
  1770. var
  1771. DirChanged: string;
  1772. begin
  1773. if Sender is TDiscMonitor then
  1774. begin
  1775. DirChanged := (Sender as TDiscMonitor).Directories[0];
  1776. if Length(DirChanged) > 0 then
  1777. begin
  1778. with GetDriveStatus(DriveInfo.GetDriveKey(DirChanged)) do
  1779. begin
  1780. ChangeTimer.Interval := 0;
  1781. ChangeTimer.Interval := FChangeInterval;
  1782. ChangeTimer.Enabled := True;
  1783. end;
  1784. end;
  1785. end;
  1786. end; {DirWatchChangeDetected}
  1787. procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
  1788. var
  1789. DriveStatusPair: TDriveStatusPair;
  1790. begin
  1791. if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
  1792. begin
  1793. for DriveStatusPair in FDriveStatus do
  1794. begin
  1795. if DriveStatusPair.Value.ChangeTimer = Sender then
  1796. begin
  1797. // Messages are processed during ValidateDirectory, so we may detect another change while
  1798. // updating the directory. Prevent the recursion.
  1799. // But retry the update afterwards (by reenabling the timer in ChangeDetected)
  1800. SuspendChangeTimer;
  1801. try
  1802. with DriveStatusPair.Value.ChangeTimer do
  1803. begin
  1804. Interval := 0;
  1805. Enabled := False;
  1806. end;
  1807. if Assigned(DriveStatusPair.Value.RootNode) then
  1808. begin
  1809. {Check also collapsed (invisible) subdirectories:}
  1810. ValidateDirectory(DriveStatusPair.Value.RootNode);
  1811. end;
  1812. finally
  1813. ResumeChangeTimer;
  1814. end;
  1815. end;
  1816. end;
  1817. end;
  1818. end; {ChangeTimerOnTimer}
  1819. procedure TDriveView.UpdateDriveNotifications(Drive: string);
  1820. var
  1821. NeedNotifications: Boolean;
  1822. Path: string;
  1823. DevBroadcastHandle: DEV_BROADCAST_HANDLE;
  1824. Size: Integer;
  1825. begin
  1826. if DriveInfo.IsFixedDrive(Drive) then
  1827. begin
  1828. with GetDriveStatus(Drive) do
  1829. begin
  1830. NeedNotifications :=
  1831. WatchThreadActive(Drive) and
  1832. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) and
  1833. DriveInfo.Get(Drive).DriveReady;
  1834. if NeedNotifications <> (DriveHandle <> INVALID_HANDLE_VALUE) then
  1835. begin
  1836. if NeedNotifications then
  1837. begin
  1838. Path := DriveInfo.GetDriveRoot(Drive);
  1839. DriveHandle :=
  1840. CreateFile(PChar(Path), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
  1841. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0);
  1842. if DriveHandle <> INVALID_HANDLE_VALUE then
  1843. begin
  1844. Size := SizeOf(DevBroadcastHandle);
  1845. ZeroMemory(@DevBroadcastHandle, Size);
  1846. DevBroadcastHandle.dbch_size := Size;
  1847. DevBroadcastHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
  1848. DevBroadcastHandle.dbch_handle := DriveHandle;
  1849. NotificationHandle :=
  1850. RegisterDeviceNotification(FInternalWindowHandle, @DevBroadcastHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  1851. if NotificationHandle = nil then
  1852. begin
  1853. CloseHandle(DriveHandle);
  1854. DriveHandle := INVALID_HANDLE_VALUE;
  1855. end;
  1856. end;
  1857. end
  1858. else
  1859. begin
  1860. UnregisterDeviceNotification(NotificationHandle);
  1861. NotificationHandle := nil;
  1862. CloseHandle(DriveHandle);
  1863. DriveHandle := INVALID_HANDLE_VALUE;
  1864. end;
  1865. end;
  1866. end;
  1867. end;
  1868. end;
  1869. procedure TDriveView.StartWatchThread;
  1870. var
  1871. Drive: string;
  1872. begin
  1873. if (csDesigning in ComponentState) or
  1874. not Assigned(Selected) or
  1875. not fWatchDirectory then Exit;
  1876. Drive := GetDriveToNode(Selected);
  1877. with GetDriveStatus(Drive) do
  1878. begin
  1879. if not Assigned(DiscMonitor) then
  1880. CreateWatchThread(Drive);
  1881. if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
  1882. DiscMonitor.Enabled := True;
  1883. end;
  1884. UpdateDriveNotifications(Drive);
  1885. end; {StartWatchThread}
  1886. procedure TDriveView.StopWatchThread;
  1887. var
  1888. Drive: string;
  1889. begin
  1890. if Assigned(Selected) then
  1891. begin
  1892. Drive := GetDriveToNode(Selected);
  1893. with GetDriveStatus(Drive) do
  1894. if Assigned(DiscMonitor) then
  1895. DiscMonitor.Enabled := False;
  1896. UpdateDriveNotifications(Drive);
  1897. end;
  1898. end; {StopWatchThread}
  1899. procedure TDriveView.SuspendChangeTimer;
  1900. begin
  1901. Inc(FChangeTimerSuspended);
  1902. end;
  1903. procedure TDriveView.ResumeChangeTimer;
  1904. begin
  1905. Assert(FChangeTimerSuspended > 0);
  1906. Dec(FChangeTimerSuspended);
  1907. end;
  1908. procedure TDriveView.TerminateWatchThread(Drive: string);
  1909. begin
  1910. with GetDriveStatus(Drive) do
  1911. if Assigned(DiscMonitor) then
  1912. begin
  1913. DiscMonitor.Free;
  1914. DiscMonitor := nil;
  1915. end;
  1916. UpdateDriveNotifications(Drive);
  1917. end; {StopWatchThread}
  1918. procedure TDriveView.StartAllWatchThreads;
  1919. var
  1920. DriveStatusPair: TDriveStatusPair;
  1921. Drive: string;
  1922. begin
  1923. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  1924. Exit;
  1925. for DriveStatusPair in FDriveStatus do
  1926. with DriveStatusPair.Value do
  1927. if Scanned then
  1928. begin
  1929. if not Assigned(DiscMonitor) then
  1930. CreateWatchThread(DriveStatusPair.Key);
  1931. if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
  1932. begin
  1933. DiscMonitor.Open;
  1934. UpdateDriveNotifications(DriveStatusPair.Key);
  1935. end;
  1936. end;
  1937. if Assigned(Selected) then
  1938. begin
  1939. Drive := GetDriveToNode(Selected);
  1940. if not DriveInfo.IsFixedDrive(Drive) then
  1941. begin
  1942. StartWatchThread;
  1943. end;
  1944. end;
  1945. end; {StartAllWatchThreads}
  1946. procedure TDriveView.StopAllWatchThreads;
  1947. var
  1948. DriveStatusPair: TDriveStatusPair;
  1949. begin
  1950. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  1951. Exit;
  1952. for DriveStatusPair in FDriveStatus do
  1953. with DriveStatusPair.Value do
  1954. begin
  1955. if Assigned(DiscMonitor) then
  1956. begin
  1957. DiscMonitor.Close;
  1958. UpdateDriveNotifications(DriveStatusPair.Key);
  1959. end;
  1960. end;
  1961. end; {StopAllWatchThreads}
  1962. function TDriveView.WatchThreadActive(Drive: string): Boolean;
  1963. begin
  1964. Result := FWatchDirectory and
  1965. Assigned(GetDriveStatus(Drive).DiscMonitor) and
  1966. GetDriveStatus(Drive).DiscMonitor.Active and
  1967. GetDriveStatus(Drive).DiscMonitor.Enabled;
  1968. end; {WatchThreadActive}
  1969. function TDriveView.WatchThreadActive: Boolean;
  1970. var
  1971. Drive: string;
  1972. begin
  1973. if not Assigned(Selected) then
  1974. begin
  1975. Result := False;
  1976. Exit;
  1977. end;
  1978. Drive := GetDriveToNode(Selected);
  1979. Result := WatchThreadActive(Drive);
  1980. end; {WatchThreadActive}
  1981. function TDriveView.FindPathNode(Path: string): TTreeNode;
  1982. var
  1983. PossiblyHiddenPath: string;
  1984. Attrs: Integer;
  1985. begin
  1986. if Assigned(FOnNeedHiddenDirectories) and
  1987. (not ShowHiddenDirs) and
  1988. DirectoryExistsFix(Path) then // do not even bother if the path does not exist
  1989. begin
  1990. PossiblyHiddenPath := ExcludeTrailingPathDelimiter(Path);
  1991. while (PossiblyHiddenPath <> '') and
  1992. (not IsRootPath(PossiblyHiddenPath)) do // Drives have hidden attribute
  1993. begin
  1994. Attrs := FileGetAttr(PossiblyHiddenPath, False);
  1995. if (Attrs and faHidden) = faHidden then
  1996. begin
  1997. if Assigned(FOnNeedHiddenDirectories) then
  1998. begin
  1999. FOnNeedHiddenDirectories(Self);
  2000. end;
  2001. Break;
  2002. end
  2003. else
  2004. begin
  2005. PossiblyHiddenPath := ExtractFileDir(PossiblyHiddenPath);
  2006. end;
  2007. end;
  2008. end;
  2009. {Find existing path or parent path of not existing path:}
  2010. repeat
  2011. Result := FindNodeToPath(Path);
  2012. if not Assigned(Result) then
  2013. Path := ExtractFilePath(ExcludeTrailingBackslash(Path));
  2014. until Assigned(Result) or (Length(Path) < 3);
  2015. end;
  2016. procedure TDriveView.SetDirectory(Value: string);
  2017. begin
  2018. Value := IncludeTrailingBackslash(Value);
  2019. FDirectory := Value;
  2020. inherited;
  2021. if Assigned(Selected) and (Selected.Level = 0) then
  2022. begin
  2023. if not GetDriveStatus(GetDriveToNode(Selected)).Scanned then
  2024. ScanDrive(GetDriveToNode(Selected));
  2025. end;
  2026. end; {SetDirectory}
  2027. function TDriveView.GetDirName(Node: TTreeNode): string;
  2028. begin
  2029. if Assigned(Node) and Assigned(Node.Data) then
  2030. Result := TNodeData(Node.Data).DirName
  2031. else
  2032. Result := '';
  2033. end; {GetDirName}
  2034. {GetDrive: returns the drive of the Node.}
  2035. function TDriveView.GetDriveToNode(Node: TTreeNode): string;
  2036. var
  2037. Path: string;
  2038. begin
  2039. if (not Assigned (Node)) or (not Assigned(Node.Data)) then
  2040. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDrive']));
  2041. Path := NodePath(Node);
  2042. Result := DriveInfo.GetDriveKey(Path);
  2043. end; {GetDrive}
  2044. {RootNode: returns the rootnode to the Node:}
  2045. function TDriveView.RootNode(Node: TTreeNode): TTreeNode;
  2046. begin
  2047. Result := Node;
  2048. if not Assigned(Node) then
  2049. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['RootNode']));
  2050. while Assigned(Result.Parent) do
  2051. Result := Result.Parent;
  2052. end; {RootNode}
  2053. function TDriveView.GetDisplayName(Node: TTreeNode): string;
  2054. begin
  2055. Result := '';
  2056. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  2057. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDisplayName']));
  2058. if Node.Level = 0 then Result := GetDriveText(GetDriveToNode(Node))
  2059. else
  2060. begin
  2061. Result := GetDirName(Node);
  2062. end;
  2063. end; {GetDisplayName}
  2064. procedure TDriveView.SetShowVolLabel(ShowIt: Boolean);
  2065. begin
  2066. if ShowIt = FShowVolLabel then
  2067. Exit;
  2068. FShowVolLabel := ShowIt;
  2069. RefreshRootNodes(dvdsFloppy);
  2070. end; {SetShowVolLabel}
  2071. procedure TDriveView.DisplayContextMenu(Node: TTreeNode; Point: TPoint);
  2072. var
  2073. Verb: string;
  2074. DirWatched: Boolean;
  2075. begin
  2076. DirWatched := NodeWatched(Node) and WatchThreadActive;
  2077. Assert(Node <> nil);
  2078. if Node <> Selected then
  2079. DropTarget := Node;
  2080. Verb := EmptyStr;
  2081. if Assigned(FOnDisplayContextMenu) then
  2082. FOnDisplayContextMenu(Self);
  2083. ShellDisplayContextMenu(FParentForm.Handle, Point, NodePathName(Node),
  2084. CanEdit(Node), Verb, False);
  2085. if Verb = shcRename then Node.EditText
  2086. else
  2087. if Verb = shcCut then
  2088. begin
  2089. LastClipBoardOperation := cboCut;
  2090. LastPathCut := NodePathName(Node);
  2091. end
  2092. else
  2093. if Verb = shcCopy then LastClipBoardOperation := cboCopy
  2094. else
  2095. if Verb = shcPaste then
  2096. PasteFromClipBoard(NodePathName(Node));
  2097. DropTarget := nil;
  2098. if not DirWatched then
  2099. ValidateDirectory(Node);
  2100. end; {DisplayContextMenu (2)}
  2101. procedure TDriveView.DisplayPropertiesMenu(Node: TTreeNode);
  2102. begin
  2103. Assert(Assigned(Node));
  2104. ShellExecuteContextCommand(FParentForm.Handle, shcProperties, NodePathName(Node));
  2105. end; {ContextMenu}
  2106. procedure TDriveView.SetSelected(Node: TTreeNode);
  2107. begin
  2108. if Node <> Selected then
  2109. begin
  2110. FChangeFlag := False;
  2111. FCanChange := True;
  2112. inherited Selected := Node;
  2113. if not FChangeFlag then
  2114. Change(Selected);
  2115. end;
  2116. end; {SetSelected}
  2117. {Called by TFileDeleteThread, when a file deletion was detected by the D&D receiving application:}
  2118. procedure TDriveView.SignalDirDelete(Sender: TObject; Files: TStringList);
  2119. begin
  2120. if Files.Count > 0 then
  2121. ValidateDirectory(FindNodeToPath(Files[0]));
  2122. end; {SignalDirDelete}
  2123. function TDriveView.DDSourceEffects: TDropEffectSet;
  2124. begin
  2125. if FDragNode.Level = 0 then
  2126. Result := [deLink]
  2127. else
  2128. Result := [deLink, deCopy, deMove];
  2129. end;
  2130. procedure TDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);
  2131. begin
  2132. if DropTarget = nil then Effect := DROPEFFECT_NONE
  2133. else
  2134. if (KeyState and (MK_CONTROL or MK_SHIFT) = 0) and (PreferredEffect = 0) then
  2135. begin
  2136. if FDragDrive <> '' then
  2137. begin
  2138. if FExeDrag and DriveInfo.IsFixedDrive(GetDriveToNode(DropTarget)) and DriveInfo.IsFixedDrive(FDragDrive) then
  2139. begin
  2140. Effect := DROPEFFECT_LINK;
  2141. end
  2142. else
  2143. if (Effect = DROPEFFECT_COPY) and
  2144. (SameText(FDragDrive, GetDriveToNode(DropTarget)) and
  2145. (FDragDropFilesEx.AvailableDropEffects and DROPEFFECT_MOVE <> 0)) then
  2146. begin
  2147. Effect := DROPEFFECT_MOVE;
  2148. end;
  2149. end;
  2150. end;
  2151. inherited;
  2152. end;
  2153. function TDriveView.DragCompleteFileList: Boolean;
  2154. begin
  2155. Result := (GetDriveTypeToNode(FDragNode) <> DRIVE_REMOVABLE);
  2156. end;
  2157. function TDriveView.DDExecute: TDragResult;
  2158. var
  2159. WatchThreadOK: Boolean;
  2160. DragParentPath: string;
  2161. DragPath: string;
  2162. begin
  2163. WatchThreadOK := WatchThreadActive;
  2164. Result := FDragDropFilesEx.Execute(nil);
  2165. if (Result = drMove) and (not WatchThreadOK) then
  2166. begin
  2167. DragPath := NodePathName(FDragNode);
  2168. if Assigned(FDragNode.Parent) then
  2169. DragParentPath := NodePathName(FDragNode.Parent)
  2170. else
  2171. DragParentPath := DragPath;
  2172. if (FDragNode.Level > 0) or (DragParentPath <> NodePathName(Selected.Parent)) then
  2173. begin
  2174. FDragNode := FindNodeToPath(DragPath);
  2175. if Assigned(FDragNode) then
  2176. begin
  2177. FDragFileList.Clear;
  2178. FDragFileList.Add(DragPath);
  2179. TFileDeleteThread.Create(FDragFileList, MaxWaitTimeOut, SignalDirDelete);
  2180. end;
  2181. end;
  2182. end;
  2183. end;
  2184. procedure TDriveView.PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer);
  2185. var
  2186. Index: Integer;
  2187. SourcePath: string;
  2188. SourceParentPath: string;
  2189. SourceIsDirectory: Boolean;
  2190. SaveCursor: TCursor;
  2191. SourceNode, TargetNode: TTreeNode;
  2192. TargetPath: string;
  2193. IsRecycleBin: Boolean;
  2194. begin
  2195. TargetPath := NodePathName(Node);
  2196. IsRecycleBin := NodeIsRecycleBin(Node);
  2197. if FDragDropFilesEx.FileList.Count = 0 then
  2198. Exit;
  2199. SaveCursor := Screen.Cursor;
  2200. Screen.Cursor := crHourGlass;
  2201. SourcePath := EmptyStr;
  2202. try
  2203. if (Effect = DROPEFFECT_COPY) or (Effect = DROPEFFECT_MOVE) then
  2204. begin
  2205. StopAllWatchThreads;
  2206. if Assigned(FDirView) then
  2207. FDirView.StopWatchThread;
  2208. if Assigned(DropSourceControl) and
  2209. (DropSourceControl is TDirView) and
  2210. (DropSourceControl <> FDirView) then
  2211. begin
  2212. TDirView(DropSourceControl).StopWatchThread;
  2213. end;
  2214. if DropFiles(
  2215. DragDropFilesEx, Effect, FFileOperator, TargetPath, false, IsRecycleBin, ConfirmDelete, ConfirmOverwrite, False,
  2216. Self, OnDDFileOperation, SourcePath, SourceIsDirectory) then
  2217. begin
  2218. if Assigned(FOnDDFileOperationExecuted) then
  2219. FOnDDFileOperationExecuted(Self, Effect, SourcePath, TargetPath);
  2220. end;
  2221. ClearDragFileList(FDragDropFilesEx.FileList);
  2222. // TDirView.PerformDragDropFileOperation validates the SourcePath and that actually seems correct
  2223. SourceParentPath := ExtractFilePath(ExcludeTrailingBackslash(SourcePath));
  2224. end
  2225. else
  2226. if Effect = DROPEFFECT_LINK then
  2227. { Create Link requested: }
  2228. begin
  2229. for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do
  2230. begin
  2231. if not DropLink(PFDDListItem(FDragDropFilesEx.FileList[Index]), TargetPath) then
  2232. begin
  2233. DDError(DDCreateShortCutError);
  2234. end;
  2235. end;
  2236. end;
  2237. if Effect = DROPEFFECT_MOVE then
  2238. Items.BeginUpdate;
  2239. {Update source directory, if move-operation was performed:}
  2240. if ((Effect = DROPEFFECT_MOVE) or IsRecycleBin) then
  2241. begin
  2242. // See comment in corresponding operation in TDirView.PerformDragDropFileOperation
  2243. SourceNode := TryFindNodeToPath(SourceParentPath);
  2244. if Assigned(SourceNode) then
  2245. ValidateDirectory(SourceNode);
  2246. end;
  2247. {Update subdirectories of target directory:}
  2248. TargetNode := FindNodeToPath(TargetPath);
  2249. if Assigned(TargetNode) then
  2250. ValidateDirectory(TargetNode)
  2251. else
  2252. ValidateDirectory(GetDriveStatus(DriveInfo.GetDriveKey(TargetPath)).RootNode);
  2253. if Effect = DROPEFFECT_MOVE then
  2254. Items.EndUpdate;
  2255. {Update linked component TDirView:}
  2256. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2257. begin
  2258. case Effect of
  2259. DROPEFFECT_COPY,
  2260. DROPEFFECT_LINK:
  2261. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) then
  2262. FDirView.Reload2;
  2263. DROPEFFECT_MOVE:
  2264. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) or
  2265. (IncludeTrailingBackslash(SourceParentPath) = IncludeTrailingBackslash(DirView.Path)) then
  2266. begin
  2267. if FDirView <> DropSourceControl then FDirView.Reload2;
  2268. end;
  2269. end; {Case}
  2270. end;
  2271. {Update the DropSource control, if files are moved and it is a TDirView:}
  2272. if (Effect = DROPEFFECT_MOVE) and (DropSourceControl is TDirView) then
  2273. begin
  2274. TDirView(DropSourceControl).ValidateSelectedFiles;
  2275. end;
  2276. finally
  2277. FFileOperator.OperandFrom.Clear;
  2278. FFileOperator.OperandTo.Clear;
  2279. StartAllWatchThreads;
  2280. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2281. FDirView.StartWatchThread;
  2282. if Assigned(DropSourceControl) and (DropSourceControl is TDirView) and
  2283. (not TDirView(DropSourceControl).WatchThreadActive) then
  2284. TDirView(DropSourceControl).StartWatchThread;
  2285. Screen.Cursor := SaveCursor;
  2286. end;
  2287. end; {PerformDragDropFileOperation}
  2288. function TDriveView.GetCanUndoCopyMove: Boolean;
  2289. begin
  2290. Result := Assigned(FFileOperator) and FFileOperator.CanUndo;
  2291. end; {CanUndoCopyMove}
  2292. function TDriveView.UndoCopyMove: Boolean;
  2293. var
  2294. LastTarget: string;
  2295. LastSource: string;
  2296. begin
  2297. Result := False;
  2298. if FFileOperator.CanUndo then
  2299. begin
  2300. Lasttarget := FFileOperator.LastOperandTo[0];
  2301. LastSource := FFileOperator.LastOperandFrom[0];
  2302. StopAllWatchThreads;
  2303. Result := FFileOperator.UndoExecute;
  2304. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastTarget)));
  2305. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastSource)));
  2306. StartAllWatchThreads;
  2307. if Assigned(FDirView) then
  2308. with FDirView do
  2309. if not WatchThreadActive then
  2310. begin
  2311. if (IncludeTrailingBackslash(ExtractFilePath(LastTarget)) = IncludeTrailingBackslash(Path)) or
  2312. (IncludeTrailingBackslash(ExtractFilePath(LastSource)) = IncludeTrailingBackslash(Path)) then
  2313. Reload2;
  2314. end;
  2315. end;
  2316. end; {UndoCopyMove}
  2317. {Clipboard operations:}
  2318. procedure TDriveView.SetLastPathCut(Path: string);
  2319. var
  2320. Node: TTreeNode;
  2321. begin
  2322. if FLastPathCut <> Path then
  2323. begin
  2324. Node := FindNodeToPath(FLastPathCut);
  2325. if Assigned(Node) then
  2326. begin
  2327. FLastPathCut := Path;
  2328. Node.Cut := False;
  2329. end;
  2330. Node := FindNodeToPath(Path);
  2331. if Assigned(Node) then
  2332. begin
  2333. FLastPathCut := Path;
  2334. Node.Cut := True;
  2335. end;
  2336. end;
  2337. end; {SetLastNodeCut}
  2338. procedure TDriveView.EmptyClipboard;
  2339. begin
  2340. if Windows.OpenClipBoard(0) then
  2341. begin
  2342. Windows.EmptyClipBoard;
  2343. Windows.CloseClipBoard;
  2344. LastPathCut := '';
  2345. LastClipBoardOperation := cboNone;
  2346. if Assigned(FDirView) then
  2347. FDirView.EmptyClipboard;
  2348. end;
  2349. end; {EmptyClipBoard}
  2350. function TDriveView.CopyToClipBoard(Node: TTreeNode): Boolean;
  2351. begin
  2352. Result := Assigned(Selected);
  2353. if Result then
  2354. begin
  2355. EmptyClipBoard;
  2356. ClearDragFileList(FDragDropFilesEx.FileList);
  2357. AddToDragFileList(FDragDropFilesEx.FileList, Selected);
  2358. Result := FDragDropFilesEx.CopyToClipBoard;
  2359. LastClipBoardOperation := cboCopy;
  2360. end;
  2361. end; {CopyToClipBoard}
  2362. function TDriveView.CutToClipBoard(Node: TTreeNode): Boolean;
  2363. begin
  2364. Result := Assigned(Node) and (Node.Level > 0) and CopyToClipBoard(Node);
  2365. if Result then
  2366. begin
  2367. LastPathCut := NodePathName(Node);
  2368. LastClipBoardOperation := cboCut;
  2369. end;
  2370. end; {CutToClipBoard}
  2371. function TDriveView.CanPasteFromClipBoard: Boolean;
  2372. begin
  2373. Result := False;
  2374. if Assigned(Selected) and Windows.OpenClipboard(0) then
  2375. begin
  2376. Result := IsClipboardFormatAvailable(CF_HDROP);
  2377. Windows.CloseClipBoard;
  2378. end;
  2379. end; {CanPasteFromClipBoard}
  2380. function TDriveView.PasteFromClipBoard(TargetPath: String = ''): Boolean;
  2381. begin
  2382. ClearDragFileList(FDragDropFilesEx.FileList);
  2383. Result := False;
  2384. if CanPasteFromClipBoard and {MP}FDragDropFilesEx.GetFromClipBoard{/MP}
  2385. then
  2386. begin
  2387. if TargetPath = '' then
  2388. TargetPath := NodePathName(Selected);
  2389. case LastClipBoardOperation of
  2390. cboCopy,
  2391. cboNone:
  2392. begin
  2393. PerformDragDropFileOperation(Selected, DROPEFFECT_COPY);
  2394. if Assigned(FOnDDExecuted) then
  2395. FOnDDExecuted(Self, DROPEFFECT_COPY);
  2396. end;
  2397. cboCut:
  2398. begin
  2399. PerformDragDropFileOperation(Selected, DROPEFFECT_MOVE);
  2400. if Assigned(FOnDDExecuted) then
  2401. FOnDDExecuted(Self, DROPEFFECT_MOVE);
  2402. EmptyClipBoard;
  2403. end;
  2404. end;
  2405. Result := True;
  2406. end;
  2407. end; {PasteFromClipBoard}
  2408. end.