DriveView.pas 86 KB

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