1
0

DriveView.pas 82 KB

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