DriveView.pas 75 KB

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