DriveView.pas 82 KB

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