DriveView.pas 85 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076
  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. OldSerial := DriveInfo.Get(Drive).DriveSerial;
  1182. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  1183. with DriveInfo.Get(Drive) do
  1184. begin
  1185. if Assigned(FDirView) and (FDirView.Path <> NewDir) then
  1186. FDirView.Path := NewDir;
  1187. if DriveReady then
  1188. begin
  1189. if not DirectoryExists(ApiPath(NewDir)) then
  1190. begin
  1191. ValidateDirectory(GetDriveStatus(Drive).RootNode);
  1192. Exit;
  1193. end;
  1194. GetDriveStatus(Drive).DefaultDir := IncludeTrailingBackslash(NewDir);
  1195. if PrevDrive <> Drive then
  1196. begin
  1197. if (PrevDrive <> '') and
  1198. (DriveInfo.Get(PrevDrive).DriveType = DRIVE_REMOVABLE) then
  1199. begin
  1200. TerminateWatchThread(PrevDrive);
  1201. end;
  1202. {Drive serial has changed or is missing: allways reread the drive:}
  1203. if (DriveSerial <> OldSerial) or (DriveSerial = 0) then
  1204. begin
  1205. if TNodeData(GetDriveStatus(Drive).RootNode.Data).Scanned then
  1206. ScanDrive(Drive);
  1207. end;
  1208. end;
  1209. StartWatchThread;
  1210. end
  1211. else {Drive not ready:}
  1212. begin
  1213. GetDriveStatus(Drive).RootNode.DeleteChildren;
  1214. GetDriveStatus(Drive).DefaultDir := EmptyStr;
  1215. end;
  1216. end;
  1217. end;
  1218. if (not Assigned(FPrevSelected)) or (not FPrevSelected.HasAsParent(Node)) then
  1219. Node.Expand(False);
  1220. FPrevSelected := Node;
  1221. ValidateCurrentDirectoryIfNotMonitoring;
  1222. end;
  1223. end;
  1224. inherited;
  1225. end; {Change}
  1226. procedure TDriveView.SetImageIndex(Node: TTreeNode);
  1227. var
  1228. FileInfo: TShFileInfo;
  1229. Drive, NodePath: string;
  1230. begin
  1231. if Assigned(Node) and TNodeData(Node.Data).IconEmpty then
  1232. begin
  1233. NodePath := NodePathName(Node);
  1234. Drive := DriveInfo.GetDriveKey(NodePath);
  1235. if not Assigned(Node.Parent) then
  1236. begin
  1237. with DriveInfo.Get(Drive) do
  1238. begin
  1239. if ImageIndex = 0 then
  1240. begin
  1241. DriveInfo.ReadDriveStatus(Drive, dsImageIndex);
  1242. Node.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1243. end
  1244. else Node.ImageIndex := ImageIndex;
  1245. Node.SelectedIndex := Node.ImageIndex;
  1246. end;
  1247. end
  1248. else
  1249. begin
  1250. if DriveInfo.Get(Drive).DriveType = DRIVE_REMOTE then
  1251. begin
  1252. Node.ImageIndex := StdDirIcon;
  1253. Node.SelectedIndex := StdDirSelIcon;
  1254. end
  1255. else
  1256. begin
  1257. try
  1258. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1259. SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  1260. if (FileInfo.iIcon < Images.Count) and (FileInfo.iIcon > 0) then
  1261. begin
  1262. Node.ImageIndex := FileInfo.iIcon;
  1263. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1264. SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_OPENICON);
  1265. Node.SelectedIndex := FileInfo.iIcon;
  1266. end
  1267. else
  1268. begin
  1269. Node.ImageIndex := StdDirIcon;
  1270. Node.SelectedIndex := StdDirSelIcon;
  1271. end;
  1272. except
  1273. Node.ImageIndex := StdDirIcon;
  1274. Node.SelectedIndex := StdDirSelIcon;
  1275. end;
  1276. end;
  1277. end;
  1278. end; {IconEmpty}
  1279. TNodeData(Node.Data).IconEmpty := False;
  1280. end; {SetImageIndex}
  1281. function TDriveView.GetDriveText(Drive: string): string;
  1282. begin
  1283. if FShowVolLabel and (Length(DriveInfo.GetPrettyName(Drive)) > 0) then
  1284. begin
  1285. case FVolDisplayStyle of
  1286. doPrettyName: Result := DriveInfo.GetPrettyName(Drive);
  1287. doDisplayName: Result := DriveInfo.GetDisplayName(Drive);
  1288. end; {Case}
  1289. end
  1290. else
  1291. begin
  1292. Result := DriveInfo.GetSimpleName(Drive);
  1293. end;
  1294. end; {GetDriveText}
  1295. function CompareDrive(List: TStringList; Index1, Index2: Integer): Integer;
  1296. var
  1297. Drive1, Drive2: string;
  1298. RealDrive1, RealDrive2: Boolean;
  1299. begin
  1300. Drive1 := List[Index1];
  1301. Drive2 := List[Index2];
  1302. RealDrive1 := DriveInfo.IsRealDrive(Drive1);
  1303. RealDrive2 := DriveInfo.IsRealDrive(Drive2);
  1304. if RealDrive1 = RealDrive2 then
  1305. begin
  1306. Result := CompareText(Drive1, Drive2);
  1307. end
  1308. else
  1309. if RealDrive1 and (not RealDrive2) then
  1310. begin
  1311. Result := -1;
  1312. end
  1313. else
  1314. begin
  1315. Result := 1;
  1316. end;
  1317. end;
  1318. function TDriveView.GetDrives: TStrings;
  1319. var
  1320. DriveStatusPair: TDriveStatusPair;
  1321. Drives: TStringList;
  1322. begin
  1323. Drives := TStringList.Create;
  1324. { We could iterate only .Keys here, but that crashes IDE for some reason }
  1325. for DriveStatusPair in FDriveStatus do
  1326. begin
  1327. Drives.Add(DriveStatusPair.Key);
  1328. end;
  1329. Drives.CustomSort(CompareDrive);
  1330. Result := Drives;
  1331. end;
  1332. procedure TDriveView.DriveRemoved(Drive: string);
  1333. var
  1334. NewDrive: Char;
  1335. begin
  1336. if (Directory <> '') and (Directory[1] = Drive) then
  1337. begin
  1338. if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
  1339. else NewDrive := SystemDrive;
  1340. repeat
  1341. if NewDrive < SystemDrive then NewDrive := SystemDrive
  1342. else
  1343. if NewDrive = SystemDrive then NewDrive := LastDrive
  1344. else Dec(NewDrive);
  1345. DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
  1346. if NewDrive = Drive then
  1347. begin
  1348. Break;
  1349. end;
  1350. if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
  1351. begin
  1352. Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
  1353. break;
  1354. end;
  1355. until False;
  1356. if not Assigned(Selected) then
  1357. begin
  1358. Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
  1359. end;
  1360. end;
  1361. end;
  1362. procedure TDriveView.DriveNotification(Notification: TDriveNotification; Drive: string);
  1363. begin
  1364. case Notification of
  1365. dnRefresh:
  1366. RefreshRootNodes;
  1367. dnRemoving:
  1368. // Lame way to reduce rick, we modify the treee while it's being updated already.
  1369. // It might be better to post an update message to the message loop.
  1370. if WatchThreadActive(Drive) then
  1371. DriveRemoving(Drive);
  1372. end;
  1373. end;
  1374. procedure TDriveView.RefreshRootNodes(Floppy: Boolean);
  1375. var
  1376. Drives: TStrings;
  1377. NewText: string;
  1378. SaveCursor: TCursor;
  1379. WasValid: Boolean;
  1380. NodeData: TNodeData;
  1381. DriveStatus: TDriveStatus;
  1382. NextDriveNode: TTreeNode;
  1383. Index: Integer;
  1384. Drive: string;
  1385. begin
  1386. SaveCursor := Screen.Cursor;
  1387. Screen.Cursor := crHourGlass;
  1388. Drives := nil;
  1389. try
  1390. Drives := GetDrives;
  1391. NextDriveNode := nil;
  1392. for Index := Drives.Count - 1 downto 0 do
  1393. begin
  1394. Drive := Drives[Index];
  1395. DriveStatus := GetDriveStatus(Drive);
  1396. if Floppy or DriveInfo.IsFixedDrive(Drive) then
  1397. begin
  1398. with DriveInfo.Get(Drive) do
  1399. begin
  1400. WasValid := Assigned(DriveStatus.RootNode);
  1401. end;
  1402. with DriveInfo.Get(Drive), DriveStatus do
  1403. begin
  1404. if Valid then
  1405. begin
  1406. if not WasValid then
  1407. {New drive has arrived: insert new rootnode:}
  1408. begin
  1409. { Create root directory node }
  1410. NodeData := TNodeData.Create;
  1411. NodeData.DirName := DriveInfo.GetDriveRoot(Drive);
  1412. if Assigned(NextDriveNode) then
  1413. RootNode := Items.InsertObject(NextDriveNode, '', NodeData)
  1414. else
  1415. RootNode := Items.AddObject(nil, '', NodeData);
  1416. RootNode.Text := GetDisplayName(RootNode);
  1417. RootNode.HasChildren := True;
  1418. Scanned := False;
  1419. Verified := False;
  1420. end
  1421. else
  1422. if RootNode.ImageIndex <> DriveInfo.Get(Drive).ImageIndex then
  1423. begin {WasValid = True}
  1424. RootNode.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1425. RootNode.SelectedIndex := DriveInfo.Get(Drive).ImageIndex;
  1426. end;
  1427. if Assigned(RootNode) then
  1428. begin
  1429. NewText := GetDisplayName(RootNode);
  1430. if RootNode.Text <> NewText then
  1431. RootNode.Text := NewText;
  1432. end;
  1433. end
  1434. else
  1435. if WasValid then
  1436. {Drive has been removed => delete rootnode:}
  1437. begin
  1438. DriveRemoved(Drive);
  1439. Scanned := False;
  1440. Verified := False;
  1441. RootNode.Delete;
  1442. RootNode := nil;
  1443. end;
  1444. end;
  1445. end;
  1446. if Assigned(DriveStatus.RootNode) then
  1447. NextDriveNode := DriveStatus.RootNode;
  1448. end;
  1449. finally
  1450. Screen.Cursor := SaveCursor;
  1451. Drives.Free;
  1452. end;
  1453. end; {RefreshRootNodes}
  1454. procedure TDriveView.AddChildNode(ParentNode: TTreeNode; ParentPath: string; SRec: TSearchRec);
  1455. var
  1456. NewNode: TTreeNode;
  1457. NodeData: TNodeData;
  1458. begin
  1459. NodeData := TNodeData.Create;
  1460. NodeData.Attr := SRec.Attr;
  1461. NodeData.DirName := SRec.Name;
  1462. NodeData.FIsRecycleBin :=
  1463. (SRec.Attr and faSysFile <> 0) and
  1464. (not Assigned(ParentNode.Parent)) and
  1465. (SameText(SRec.Name, 'RECYCLED') or
  1466. SameText(SRec.Name, 'RECYCLER') or
  1467. SameText(SRec.Name, '$RECYCLE.BIN'));
  1468. NodeData.Scanned := False;
  1469. NewNode := Self.Items.AddChildObject(ParentNode, '', NodeData);
  1470. NewNode.Text := GetDisplayName(NewNode);
  1471. NewNode.HasChildren := True;
  1472. if GetDriveTypeToNode(ParentNode) <> DRIVE_REMOTE then
  1473. FSubDirReaderThread.Add(NewNode, IncludeTrailingBackslash(ParentPath) + SRec.Name);
  1474. end; {AddChildNode}
  1475. function TDriveView.GetDriveStatus(Drive: string): TDriveStatus;
  1476. begin
  1477. if not FDriveStatus.TryGetValue(Drive, Result) then
  1478. begin
  1479. Result := CreateDriveStatus;
  1480. FDriveStatus.Add(Drive, Result);
  1481. end;
  1482. end; {GetDriveStatus}
  1483. function TDriveView.DoScanDir(FromNode: TTreeNode): Boolean;
  1484. begin
  1485. Result := not TNodeData(FromNode.Data).IsRecycleBin;
  1486. end; {DoScanDir}
  1487. function TDriveView.DirAttrMask: Integer;
  1488. begin
  1489. Result := faDirectory or faSysFile;
  1490. if ShowHiddenDirs then
  1491. Result := Result or faHidden;
  1492. end;
  1493. procedure TDriveView.ScanDrive(Drive: string);
  1494. begin {ScanDrive}
  1495. with Self.Items do
  1496. begin
  1497. ValidateDirectory(FindNodeToPath(DriveInfo.GetDriveRoot(Drive)));
  1498. GetDriveStatus(Drive).Scanned := True;
  1499. GetDriveStatus(Drive).Verified := False;
  1500. end;
  1501. end; {ScanDrive}
  1502. function TDriveView.DoFindNodeToPath(Path: string; ExistingOnly: Boolean): TTreeNode;
  1503. var
  1504. SelectionHierarchy: array of TTreeNode;
  1505. SelectionHierarchyHeight: Integer;
  1506. function SearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode; forward;
  1507. function ExtractFirstName(S: string): string;
  1508. var
  1509. I: Integer;
  1510. begin
  1511. I := Pos('\', S);
  1512. if I = 0 then
  1513. I := Length(S);
  1514. Result := System.Copy(S, 1, I);
  1515. end;
  1516. function DoSearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode;
  1517. var
  1518. Node: TTreeNode;
  1519. Dir: string;
  1520. begin
  1521. {Extract first directory from path:}
  1522. Dir := ExtractFirstName(Path);
  1523. System.Delete(Path, 1, Length(Dir));
  1524. if Dir[Length(Dir)] = '\' then
  1525. SetLength(Dir, Pred(Length(Dir)));
  1526. // Optimization. Avoid iterating possibly thousands of nodes,
  1527. // when the node we are looking for is the selected node or its ancestor.
  1528. // This is often the case, when navigating under node that has lot of siblings.
  1529. // Typically, when navigating in user's profile folder, and there are many [thousands] other user profile folders.
  1530. if (SelectionHierarchyHeight > 0) and
  1531. // Change of selection might indicate that the tree was rebuilt meanwhile and
  1532. // the references in SelectionHierarchy might not be valid anymore
  1533. (Selected = SelectionHierarchy[SelectionHierarchyHeight - 1]) and
  1534. (Level < SelectionHierarchyHeight) and
  1535. (Uppercase(GetDirName(SelectionHierarchy[Level])) = Dir) then
  1536. begin
  1537. Result := SelectionHierarchy[Level];
  1538. end
  1539. else
  1540. begin
  1541. // Paths have diverted
  1542. SelectionHierarchyHeight := 0;
  1543. Node := ParentNode.GetFirstChild;
  1544. if (not Assigned(Node)) and (not ExistingOnly) then
  1545. begin
  1546. ValidateDirectoryEx(ParentNode, rsRecursiveExisting, True);
  1547. Node := ParentNode.GetFirstChild;
  1548. end;
  1549. Result := nil;
  1550. while (not Assigned(Result)) and Assigned(Node) do
  1551. begin
  1552. if UpperCase(GetDirName(Node)) = Dir then
  1553. begin
  1554. Result := Node;
  1555. end
  1556. else
  1557. begin
  1558. Node := ParentNode.GetNextChild(Node);
  1559. end;
  1560. end;
  1561. end;
  1562. if Assigned(Result) and (Length(Path) > 0) then
  1563. begin
  1564. Result := SearchSubDirs(Result, Path, Level + 1);
  1565. end;
  1566. end;
  1567. function SearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode;
  1568. var
  1569. ParentPath, SubPath: string;
  1570. SRec: TSearchRec;
  1571. ParentNodeData: TNodeData;
  1572. begin
  1573. Result := nil;
  1574. if Length(Path) > 0 then
  1575. begin
  1576. ParentNodeData := TNodeData(ParentNode.Data);
  1577. if (not ParentNodeData.Scanned) and (not ExistingOnly) then
  1578. begin
  1579. ReadSubDirs(ParentNode);
  1580. end;
  1581. // Factored out of DoSearchSubDirs is remnant of Bug 956 superceded by Bug 1320
  1582. Result := DoSearchSubDirs(ParentNode, Path, Level);
  1583. if (not Assigned(Result)) and (not ExistingOnly) then
  1584. begin
  1585. ParentPath := NodePath(ParentNode);
  1586. if DirectoryExists(ApiPath(IncludeTrailingBackslash(ParentPath) + Path)) then
  1587. begin
  1588. SubPath := IncludeTrailingBackslash(ParentPath) + ExcludeTrailingBackslash(ExtractFirstName(Path));
  1589. if FindFirstSubDir(SubPath, SRec) then
  1590. begin
  1591. AddChildNode(ParentNode, ParentPath, SRec);
  1592. if Assigned(ParentNodeData.DelayedExclude) then
  1593. ParentNodeData.DelayedExclude.Add(SRec.Name);
  1594. SortChildren(ParentNode, False);
  1595. FindClose(SRec);
  1596. end;
  1597. Result := DoSearchSubDirs(ParentNode, Path, Level);
  1598. end;
  1599. end;
  1600. end;
  1601. end; {SearchSubDirs}
  1602. var
  1603. Drive: string;
  1604. P, I: Integer;
  1605. RootNode, Node: TTreeNode;
  1606. begin {FindNodeToPath}
  1607. Result := nil;
  1608. if Length(Path) < 3 then
  1609. Exit;
  1610. // Particularly when used by TDirView to delegate browsing to
  1611. // hidden drive view, the handle may not be created
  1612. HandleNeeded;
  1613. Drive := DriveInfo.GetDriveKey(Path);
  1614. // Likely a network drive
  1615. if (not Assigned(GetDriveStatus(Drive).RootNode)) and
  1616. // Side effect of this is drive refresh that adds the network drive to the trees
  1617. DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy then
  1618. begin
  1619. // This refreshes the drives again
  1620. DriveInfo.OverrideDrivePolicy(Drive);
  1621. end;
  1622. // if not assigned now, it must be that the drive already existed in DriveInfo, but it didn't make it to this view
  1623. // (possible a network drive opened in another panel before)
  1624. if not Assigned(GetDriveStatus(Drive).RootNode) then
  1625. begin
  1626. // Refresh the view drives to add the new drive and also explorer's drive drop down.
  1627. // Overkill, as we know exactly what drive to add (so not need to check all drives)
  1628. DriveInfo.DriveRefresh;
  1629. end;
  1630. if Assigned(GetDriveStatus(Drive).RootNode) then
  1631. begin
  1632. if DriveInfo.IsRealDrive(Drive) then
  1633. begin
  1634. System.Delete(Path, 1, 3);
  1635. end
  1636. else
  1637. if IsUncPath(Path) then
  1638. begin
  1639. System.Delete(Path, 1, 2);
  1640. P := Pos('\', Path);
  1641. if P = 0 then
  1642. begin
  1643. Path := '';
  1644. end
  1645. else
  1646. begin
  1647. System.Delete(Path, 1, P);
  1648. P := Pos('\', Path);
  1649. if P = 0 then
  1650. begin
  1651. Path := '';
  1652. end
  1653. else
  1654. begin
  1655. System.Delete(Path, 1, P);
  1656. end;
  1657. end;
  1658. end
  1659. else
  1660. begin
  1661. raise EConvertError.Create(Format(ErrorInvalidDrive, [Path]))
  1662. end;
  1663. if Length(Path) > 0 then
  1664. begin
  1665. if (not GetDriveStatus(Drive).Scanned) and (not ExistingOnly) then
  1666. begin
  1667. ScanDrive(Drive);
  1668. end;
  1669. Node := Selected;
  1670. RootNode := GetDriveStatus(Drive).RootNode;
  1671. if not Assigned(Node) then
  1672. begin
  1673. SelectionHierarchyHeight := 0;
  1674. end
  1675. else
  1676. begin
  1677. SelectionHierarchyHeight := Node.Level + 1;
  1678. SetLength(SelectionHierarchy, SelectionHierarchyHeight);
  1679. for I := SelectionHierarchyHeight - 1 downto 0 do
  1680. begin
  1681. SelectionHierarchy[I] := Node;
  1682. Node := Node.Parent;
  1683. end;
  1684. Assert(Selected = SelectionHierarchy[SelectionHierarchyHeight - 1]);
  1685. // Different drive - nothing to optimize
  1686. if RootNode <> SelectionHierarchy[0] then
  1687. SelectionHierarchyHeight := 0;
  1688. end;
  1689. Result := SearchSubDirs(RootNode, UpperCase(Path), 1);
  1690. end
  1691. else Result := GetDriveStatus(Drive).RootNode;
  1692. end;
  1693. end; {FindNodetoPath}
  1694. function TDriveView.FindNodeToPath(Path: string): TTreeNode;
  1695. begin
  1696. Result := DoFindNodeToPath(Path, False);
  1697. end;
  1698. function TDriveView.TryFindNodeToPath(Path: string): TTreeNode;
  1699. begin
  1700. Result := DoFindNodeToPath(Path, True);
  1701. end;
  1702. function TDriveView.GetSubDir(var SRec: TSearchRec): Boolean;
  1703. begin
  1704. Result := True;
  1705. while Result and
  1706. ((SRec.Name = '.' ) or
  1707. (SRec.Name = '..') or
  1708. ((SRec.Attr and faDirectory) = 0)) do
  1709. begin
  1710. if FindNext(SRec) <> 0 then
  1711. begin
  1712. Result := False;
  1713. end;
  1714. end;
  1715. end;
  1716. function TDriveView.FindFirstSubDir(Path: string; var SRec: TSearchRec): Boolean;
  1717. begin
  1718. Result := (FindFirstEx(ApiPath(Path), DirAttrMask, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS, FindExSearchLimitToDirectories) = 0);
  1719. if Result then
  1720. begin
  1721. Result := GetSubDir(SRec);
  1722. if not Result then FindClose(SRec);
  1723. end;
  1724. end;
  1725. function TDriveView.FindNextSubDir(var SRec: TSearchRec): Boolean;
  1726. begin
  1727. Result := (FindNext(SRec) = 0) and GetSubDir(SRec);
  1728. end;
  1729. function TDriveView.ReadSubDirsBatch(Node: TTreeNode; var SRec: TSearchRec; CheckInterval, Limit: Integer): Boolean;
  1730. var
  1731. Start: TDateTime;
  1732. Cont: Boolean;
  1733. Path: string;
  1734. Count: Integer;
  1735. DelayedExclude: TStringList;
  1736. begin
  1737. Start := Now;
  1738. Path := NodePath(Node);
  1739. Result := True;
  1740. Count := 0;
  1741. DelayedExclude := TNodeData(Node.Data).DelayedExclude;
  1742. // At least from SetDirectory > DoFindNodeToPath and CanExpand, this is not called within BeginUpdate/EndUpdate block.
  1743. // But in any case, adding it here makes expanding (which calls CanExpand) noticeably slower, when there are lot of nodes,
  1744. // because EndUpdate triggers TVN_GETDISPINFO for all nodes in the tree.
  1745. repeat
  1746. if (not Assigned(DelayedExclude)) or
  1747. (DelayedExclude.IndexOf(SRec.Name) < 0) then
  1748. begin
  1749. AddChildNode(Node, Path, SRec);
  1750. Inc(Count);
  1751. end;
  1752. Cont := FindNextSubDir(SRec);
  1753. // There are two other directory reading loops, where this is not checked
  1754. if Cont and
  1755. ((Count mod CheckInterval) = 0) and
  1756. (Limit > 0) and
  1757. (MilliSecondsBetween(Now, Start) > Limit) then
  1758. begin
  1759. Result := False;
  1760. Cont := False;
  1761. end
  1762. until not Cont;
  1763. if Result then
  1764. FindClose(Srec);
  1765. end;
  1766. procedure TDriveView.DelayedNodeTimer(Sender: TObject);
  1767. var
  1768. Node: TTreeNode;
  1769. NodeData: TNodeData;
  1770. begin
  1771. Assert(FDelayedNodes.Count > 0);
  1772. if FDelayedNodes.Count > 0 then
  1773. begin
  1774. // Control was recreated
  1775. if not Assigned(FDelayedNodes.Objects[0]) then
  1776. begin
  1777. FDelayedNodes.Objects[0] := TryFindNodeToPath(FDelayedNodes.Strings[0]);
  1778. end;
  1779. Node := TTreeNode(FDelayedNodes.Objects[0]);
  1780. if not Assigned(Node) then
  1781. begin
  1782. FDelayedNodes.Delete(0);
  1783. end
  1784. else
  1785. begin
  1786. NodeData := TNodeData(Node.Data);
  1787. if ReadSubDirsBatch(Node, NodeData.DelayedSrec, 10, 50) then
  1788. begin
  1789. FreeAndNil(NodeData.DelayedExclude);
  1790. FDelayedNodes.Delete(0);
  1791. SortChildren(Node, False);
  1792. end;
  1793. end;
  1794. end;
  1795. UpdateDelayedNodeTimer;
  1796. end;
  1797. procedure TDriveView.UpdateDelayedNodeTimer;
  1798. begin
  1799. FDelayedNodeTimer.Enabled := HandleAllocated and (FDelayedNodes.Count > 0);
  1800. end;
  1801. procedure TDriveView.ReadSubDirs(Node: TTreeNode);
  1802. var
  1803. SRec: TSearchRec;
  1804. NodeData: TNodeData;
  1805. Path: string;
  1806. CheckInterval, Limit: Integer;
  1807. begin
  1808. NodeData := TNodeData(Node.Data);
  1809. Path := NodePath(Node);
  1810. if not FindFirstSubDir(IncludeTrailingBackslash(Path) + '*.*', SRec) then
  1811. begin
  1812. Node.HasChildren := False;
  1813. end
  1814. else
  1815. begin
  1816. CheckInterval := 100;
  1817. Limit := DriveViewLoadingTooLongLimit * 1000;
  1818. if not Showing then
  1819. begin
  1820. Limit := Limit div 10;
  1821. CheckInterval := CheckInterval div 10;
  1822. end;
  1823. if not ReadSubDirsBatch(Node, SRec, CheckInterval, Limit) then
  1824. begin
  1825. NodeData.DelayedSrec := SRec;
  1826. NodeData.DelayedExclude := TStringList.Create;
  1827. NodeData.DelayedExclude.CaseSensitive := False;
  1828. NodeData.DelayedExclude.Sorted := True;
  1829. FDelayedNodes.AddObject(Path, Node);
  1830. Assert(FDelayedNodes.Count < 20); // if more, something went likely wrong
  1831. UpdateDelayedNodeTimer;
  1832. end;
  1833. SortChildren(Node, False);
  1834. end;
  1835. NodeData.Scanned := True;
  1836. Application.ProcessMessages;
  1837. end; {ReadSubDirs}
  1838. procedure TDriveView.CancelDelayedNode(Node: TTreeNode);
  1839. var
  1840. NodeData: TNodeData;
  1841. begin
  1842. NodeData := TNodeData(Node.Data);
  1843. FindClose(NodeData.DelayedSrec);
  1844. FreeAndNil(NodeData.DelayedExclude);
  1845. end;
  1846. procedure TDriveView.DeleteNode(Node: TTreeNode);
  1847. var
  1848. ValidNode: TTreeNode;
  1849. begin
  1850. if Assigned(Selected) and Assigned(Node.Parent) and
  1851. ((Selected = Node) or Selected.HasAsParent(Node)) then
  1852. begin
  1853. ValidNode := Node.Parent;
  1854. while (not NodePathExists(ValidNode)) and Assigned(ValidNode.Parent) do
  1855. ValidNode := ValidNode.Parent;
  1856. Selected := ValidNode;
  1857. end;
  1858. if DropTarget = Node then
  1859. DropTarget := nil;
  1860. Node.Delete;
  1861. end;
  1862. function TDriveView.CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  1863. var
  1864. WorkNode: TTreeNode;
  1865. DelNode: TTreeNode;
  1866. SRec: TSearchRec;
  1867. SrecList: TStringList;
  1868. SubDirList: TStringList;
  1869. R: Boolean;
  1870. Index: Integer;
  1871. NewDirFound: Boolean;
  1872. ParentDir: string;
  1873. NodeData: TNodeData;
  1874. ScanDirInfo: PScanDirInfo;
  1875. begin {CallBackValidateDir}
  1876. Result := True;
  1877. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  1878. Exit;
  1879. NewDirFound := False;
  1880. ScanDirInfo := PScanDirInfo(Data);
  1881. {Check, if directory still exists: (but not with root directory) }
  1882. if Assigned(Node.Parent) and (ScanDirInfo^.StartNode = Node) and
  1883. (not NodePathExists(Node)) then
  1884. begin
  1885. DeleteNode(Node);
  1886. Node := nil;
  1887. Exit;
  1888. end;
  1889. WorkNode := Node.GetFirstChild;
  1890. NodeData := TNodeData(Node.Data);
  1891. if NodeData.Scanned and Assigned(WorkNode) then
  1892. {if node was already scanned: check wether the existing subnodes are still alive
  1893. and add all new subdirectories as subnodes:}
  1894. begin
  1895. if DoScanDir(Node) then
  1896. begin
  1897. ParentDir := IncludeTrailingBackslash(NodePath(Node));
  1898. {Build list of existing subnodes:}
  1899. SubDirList := TStringList.Create;
  1900. SubDirList.CaseSensitive := True; // We want to reflect changes in subfolder name case
  1901. while Assigned(WorkNode) do
  1902. begin
  1903. SubDirList.Add(TNodeData(WorkNode.Data).DirName);
  1904. WorkNode := Node.GetNextChild(WorkNode);
  1905. end;
  1906. // Nodes are sorted using natural sorting, while TStringList.Find uses simple sorting
  1907. SubDirList.Sort;
  1908. SRecList := TStringList.Create;
  1909. SRecList.CaseSensitive := True;
  1910. R := FindFirstSubDir(ParentDir + '*.*', SRec);
  1911. while R do
  1912. begin
  1913. SrecList.Add(Srec.Name);
  1914. if not SubDirList.Find(Srec.Name, Index) then
  1915. {Subnode does not exists: add it:}
  1916. begin
  1917. AddChildNode(Node, ParentDir, SRec);
  1918. NewDirFound := True;
  1919. end;
  1920. R := FindNextSubDir(Srec);
  1921. end;
  1922. FindClose(Srec);
  1923. Sreclist.Sort;
  1924. {Remove not existing subnodes:}
  1925. WorkNode := Node.GetFirstChild;
  1926. while Assigned(WorkNode) do
  1927. begin
  1928. if not Assigned(WorkNode.Data) or
  1929. not SrecList.Find(TNodeData(WorkNode.Data).DirName, Index) then
  1930. begin
  1931. DelNode := WorkNode;
  1932. WorkNode := Node.GetNextChild(WorkNode);
  1933. DeleteNode(DelNode);
  1934. end
  1935. else
  1936. begin
  1937. if (SrecList[Index] <> TNodeData(WorkNode.Data).DirName) then
  1938. begin
  1939. {Case of directory letters has changed:}
  1940. TNodeData(WorkNode.Data).DirName := SrecList[Index];
  1941. WorkNode.Text := SrecList[Index];
  1942. end;
  1943. WorkNode := Node.GetNextChild(WorkNode);
  1944. end;
  1945. end;
  1946. SrecList.Free;
  1947. SubDirList.Free;
  1948. {Sort subnodes:}
  1949. if NewDirFound then
  1950. SortChildren(Node, False);
  1951. end;
  1952. end
  1953. else
  1954. {Node was not already scanned:}
  1955. if (ScanDirInfo^.SearchNewDirs or
  1956. NodeData.Scanned or
  1957. (Node = ScanDirInfo^.StartNode)) and
  1958. DoScanDir(Node) then
  1959. begin
  1960. ReadSubDirs(Node);
  1961. end;
  1962. end; {CallBackValidateDir}
  1963. procedure TDriveView.RebuildTree;
  1964. var
  1965. Drive: string;
  1966. begin
  1967. for Drive in FDriveStatus.Keys do
  1968. with GetDriveStatus(Drive) do
  1969. if Assigned(RootNode) and Scanned then
  1970. ValidateDirectory(RootNode);
  1971. end;
  1972. procedure TDriveView.ValidateCurrentDirectoryIfNotMonitoring;
  1973. begin
  1974. if Assigned(Selected) and
  1975. not Assigned(GetDriveStatus(GetDriveToNode(Selected)).DiscMonitor) then
  1976. begin
  1977. ValidateDirectory(Selected);
  1978. end;
  1979. end;
  1980. procedure TDriveView.ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  1981. NewDirs: Boolean);
  1982. var
  1983. Info: PScanDirInfo;
  1984. SelDir: string;
  1985. SaveCursor: TCursor;
  1986. RestartWatchThread: Boolean;
  1987. SaveCanChange: Boolean;
  1988. CurrentPath: string;
  1989. Drive: string;
  1990. begin
  1991. if Assigned(Node) and Assigned(Node.Data) and
  1992. (not FValidateFlag) and DoScanDir(Node) then
  1993. begin
  1994. SelDir := Directory;
  1995. SaveCursor := Screen.Cursor;
  1996. if Self.Focused and (Screen.Cursor <> crHourGlass) then
  1997. Screen.Cursor := crHourGlass;
  1998. CurrentPath := NodePath(Node);
  1999. Drive := DriveInfo.GetDriveKey(CurrentPath);
  2000. if not Assigned(Node.Parent) then
  2001. GetDriveStatus(Drive).ChangeTimer.Enabled := False;
  2002. RestartWatchThread := WatchThreadActive;
  2003. try
  2004. if WatchThreadActive then
  2005. StopWatchThread;
  2006. FValidateFlag := True;
  2007. FSysColorChangePending := False;
  2008. New(Info);
  2009. Info^.StartNode := Node;
  2010. Info^.SearchNewDirs := NewDirs;
  2011. Info^.DriveType := DriveInfo.Get(Drive).DriveType;
  2012. SaveCanChange := FCanChange;
  2013. FCanChange := True;
  2014. FChangeFlag := False;
  2015. Items.BeginUpdate;
  2016. try
  2017. IterateSubTree(Node, CallBackValidateDir, Recurse, coScanStartNode, Info);
  2018. finally
  2019. Items.EndUpdate;
  2020. end;
  2021. FValidateFlag := False;
  2022. if (not Assigned(Selected)) and (Length(SelDir) > 0) then
  2023. Directory := ExtractFileDrive(SelDir);
  2024. if (SelDir <> Directory) and (not FChangeFlag) then
  2025. Change(Selected);
  2026. FCanChange := SaveCanChange;
  2027. Dispose(Info);
  2028. finally
  2029. if RestartWatchThread and FWatchDirectory and not WatchThreadActive then
  2030. StartWatchThread;
  2031. if Screen.Cursor <> SaveCursor then
  2032. Screen.Cursor := SaveCursor;
  2033. if FSysColorChangePending then
  2034. begin
  2035. FSysColorChangePending := False;
  2036. if HandleAllocated then Perform(CM_SYSCOLORCHANGE, 0, 0);
  2037. end;
  2038. end;
  2039. end;
  2040. end; {ValidateDirectoryEx}
  2041. function TDriveView.GetDriveTypeToNode(Node: TTreeNode): Integer;
  2042. begin
  2043. Assert(Assigned(Node));
  2044. Result := DriveInfo.Get(GetDriveToNode(Node)).DriveType;
  2045. end; {GetDriveTypeToNode}
  2046. procedure TDriveView.CreateWatchThread(Drive: string);
  2047. begin
  2048. if csDesigning in ComponentState then
  2049. Exit;
  2050. if (not Assigned(GetDriveStatus(Drive).DiscMonitor)) and
  2051. FWatchDirectory and
  2052. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) then
  2053. begin
  2054. with GetDriveStatus(Drive) do
  2055. begin
  2056. DiscMonitor := TDiscMonitor.Create(Self);
  2057. DiscMonitor.ChangeDelay := msThreadChangeDelay;
  2058. DiscMonitor.SubTree := True;
  2059. DiscMonitor.Filters := [moDirName];
  2060. DiscMonitor.OnChange := ChangeDetected;
  2061. DiscMonitor.OnInvalid := ChangeInvalid;
  2062. DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
  2063. DiscMonitor.Open;
  2064. end;
  2065. SubscribeDriveNotifications(Drive);
  2066. end;
  2067. end; {CreateWatchThread}
  2068. procedure TDriveView.SetWatchDirectory(Value: Boolean);
  2069. begin
  2070. if FWatchDirectory <> Value then
  2071. begin
  2072. FWatchDirectory := Value;
  2073. if (not (csDesigning in ComponentState)) and Value then
  2074. StartAllWatchThreads
  2075. else
  2076. StopAllWatchThreads;
  2077. end;
  2078. end; {SetAutoScan}
  2079. procedure TDriveView.SetDirView(Value: TDirView);
  2080. begin
  2081. if Assigned(FDirView) then
  2082. FDirView.DriveView := nil;
  2083. FDirView := Value;
  2084. if Assigned(FDirView) then
  2085. FDirView.DriveView := Self;
  2086. end; {SetDirView}
  2087. function TDriveView.NodeWatched(Node: TTreeNode): Boolean;
  2088. var
  2089. Drive: string;
  2090. begin
  2091. Drive := GetDriveToNode(Node);
  2092. Result := WatchThreadActive(Drive);
  2093. end; {NodeWatched}
  2094. procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
  2095. const ErrorStr: string);
  2096. var
  2097. Drive: string;
  2098. begin
  2099. Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
  2100. with GetDriveStatus(Drive) do
  2101. begin
  2102. DiscMonitor.Close;
  2103. end;
  2104. end; {DirWatchChangeInvalid}
  2105. procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
  2106. var SubdirsChanged: Boolean);
  2107. var
  2108. DirChanged: string;
  2109. begin
  2110. if Sender is TDiscMonitor then
  2111. begin
  2112. DirChanged := (Sender as TDiscMonitor).Directories[0];
  2113. if Length(DirChanged) > 0 then
  2114. begin
  2115. with GetDriveStatus(DriveInfo.GetDriveKey(DirChanged)) do
  2116. begin
  2117. ChangeTimer.Interval := 0;
  2118. ChangeTimer.Interval := FChangeInterval;
  2119. ChangeTimer.Enabled := True;
  2120. end;
  2121. end;
  2122. end;
  2123. end; {DirWatchChangeDetected}
  2124. procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
  2125. var
  2126. DriveStatusPair: TDriveStatusPair;
  2127. begin
  2128. if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
  2129. begin
  2130. for DriveStatusPair in FDriveStatus do
  2131. begin
  2132. if DriveStatusPair.Value.ChangeTimer = Sender then
  2133. begin
  2134. // Messages are processed during ValidateDirectory, so we may detect another change while
  2135. // updating the directory. Prevent the recursion.
  2136. // But retry the update afterwards (by reenabling the timer in ChangeDetected)
  2137. SuspendChangeTimer;
  2138. try
  2139. with DriveStatusPair.Value.ChangeTimer do
  2140. begin
  2141. Interval := 0;
  2142. Enabled := False;
  2143. end;
  2144. if Assigned(DriveStatusPair.Value.RootNode) then
  2145. begin
  2146. {Check also collapsed (invisible) subdirectories:}
  2147. ValidateDirectory(DriveStatusPair.Value.RootNode);
  2148. end;
  2149. finally
  2150. ResumeChangeTimer;
  2151. end;
  2152. end;
  2153. end;
  2154. end;
  2155. end; {ChangeTimerOnTimer}
  2156. procedure TDriveView.SubscribeDriveNotifications(Drive: string);
  2157. begin
  2158. // As previously, subscribe drive notification only once the drive is at least once visited.
  2159. // Shouldn't do much harm to subscribe always, but just in case.
  2160. DriveInfo.SubscribeDriveNotifications(Drive);
  2161. end;
  2162. procedure TDriveView.StartWatchThread;
  2163. var
  2164. Drive: string;
  2165. begin
  2166. if (csDesigning in ComponentState) or
  2167. not Assigned(Selected) or
  2168. not fWatchDirectory then Exit;
  2169. Drive := GetDriveToNode(Selected);
  2170. with GetDriveStatus(Drive) do
  2171. begin
  2172. if not Assigned(DiscMonitor) then
  2173. CreateWatchThread(Drive);
  2174. if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
  2175. DiscMonitor.Enabled := True;
  2176. end;
  2177. SubscribeDriveNotifications(Drive);
  2178. end; {StartWatchThread}
  2179. procedure TDriveView.StopWatchThread;
  2180. var
  2181. Drive: string;
  2182. begin
  2183. if Assigned(Selected) then
  2184. begin
  2185. Drive := GetDriveToNode(Selected);
  2186. with GetDriveStatus(Drive) do
  2187. if Assigned(DiscMonitor) then
  2188. DiscMonitor.Enabled := False;
  2189. end;
  2190. end; {StopWatchThread}
  2191. procedure TDriveView.SuspendChangeTimer;
  2192. begin
  2193. Inc(FChangeTimerSuspended);
  2194. end;
  2195. procedure TDriveView.ResumeChangeTimer;
  2196. begin
  2197. Assert(FChangeTimerSuspended > 0);
  2198. Dec(FChangeTimerSuspended);
  2199. end;
  2200. procedure TDriveView.TerminateWatchThread(Drive: string);
  2201. begin
  2202. with GetDriveStatus(Drive) do
  2203. if Assigned(DiscMonitor) then
  2204. begin
  2205. DiscMonitor.Free;
  2206. DiscMonitor := nil;
  2207. end;
  2208. end; {StopWatchThread}
  2209. procedure TDriveView.StartAllWatchThreads;
  2210. var
  2211. DriveStatusPair: TDriveStatusPair;
  2212. Drive: string;
  2213. begin
  2214. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2215. Exit;
  2216. for DriveStatusPair in FDriveStatus do
  2217. with DriveStatusPair.Value do
  2218. if Scanned then
  2219. begin
  2220. if not Assigned(DiscMonitor) then
  2221. CreateWatchThread(DriveStatusPair.Key);
  2222. if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
  2223. begin
  2224. DiscMonitor.Open;
  2225. SubscribeDriveNotifications(DriveStatusPair.Key);
  2226. end;
  2227. end;
  2228. if Assigned(Selected) then
  2229. begin
  2230. Drive := GetDriveToNode(Selected);
  2231. if not DriveInfo.IsFixedDrive(Drive) then
  2232. begin
  2233. StartWatchThread;
  2234. end;
  2235. end;
  2236. end; {StartAllWatchThreads}
  2237. procedure TDriveView.StopAllWatchThreads;
  2238. var
  2239. DriveStatusPair: TDriveStatusPair;
  2240. begin
  2241. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2242. Exit;
  2243. for DriveStatusPair in FDriveStatus do
  2244. with DriveStatusPair.Value do
  2245. begin
  2246. if Assigned(DiscMonitor) then
  2247. begin
  2248. DiscMonitor.Close;
  2249. end;
  2250. end;
  2251. end; {StopAllWatchThreads}
  2252. function TDriveView.WatchThreadActive(Drive: string): Boolean;
  2253. begin
  2254. Result := FWatchDirectory and
  2255. Assigned(GetDriveStatus(Drive).DiscMonitor) and
  2256. GetDriveStatus(Drive).DiscMonitor.Active and
  2257. GetDriveStatus(Drive).DiscMonitor.Enabled;
  2258. end; {WatchThreadActive}
  2259. function TDriveView.WatchThreadActive: Boolean;
  2260. var
  2261. Drive: string;
  2262. begin
  2263. if not Assigned(Selected) then
  2264. begin
  2265. Result := False;
  2266. Exit;
  2267. end;
  2268. Drive := GetDriveToNode(Selected);
  2269. Result := WatchThreadActive(Drive);
  2270. end; {WatchThreadActive}
  2271. function TDriveView.FindPathNode(Path: string): TTreeNode;
  2272. var
  2273. PossiblyHiddenPath: string;
  2274. Attrs: Integer;
  2275. begin
  2276. if Assigned(FOnNeedHiddenDirectories) and
  2277. (not ShowHiddenDirs) and
  2278. DirectoryExistsFix(Path) then // do not even bother if the path does not exist
  2279. begin
  2280. PossiblyHiddenPath := ExcludeTrailingPathDelimiter(Path);
  2281. while (PossiblyHiddenPath <> '') and
  2282. (not IsRootPath(PossiblyHiddenPath)) do // Drives have hidden attribute
  2283. begin
  2284. Attrs := FileGetAttr(PossiblyHiddenPath, False);
  2285. if (Attrs and faHidden) = faHidden then
  2286. begin
  2287. if Assigned(FOnNeedHiddenDirectories) then
  2288. begin
  2289. FOnNeedHiddenDirectories(Self);
  2290. end;
  2291. Break;
  2292. end
  2293. else
  2294. begin
  2295. PossiblyHiddenPath := ExtractFileDir(PossiblyHiddenPath);
  2296. end;
  2297. end;
  2298. end;
  2299. {Find existing path or parent path of not existing path:}
  2300. repeat
  2301. Result := FindNodeToPath(Path);
  2302. if not Assigned(Result) then
  2303. Path := ExtractFilePath(ExcludeTrailingBackslash(Path));
  2304. until Assigned(Result) or (Length(Path) < 3);
  2305. end;
  2306. procedure TDriveView.SetDirectory(Value: string);
  2307. begin
  2308. Value := IncludeTrailingBackslash(Value);
  2309. FDirectory := Value;
  2310. inherited;
  2311. if Assigned(Selected) and (not Assigned(Selected.Parent)) then
  2312. begin
  2313. if not GetDriveStatus(GetDriveToNode(Selected)).Scanned then
  2314. ScanDrive(GetDriveToNode(Selected));
  2315. end;
  2316. end; {SetDirectory}
  2317. function TDriveView.GetDirName(Node: TTreeNode): string;
  2318. begin
  2319. if Assigned(Node) and Assigned(Node.Data) then
  2320. Result := TNodeData(Node.Data).DirName
  2321. else
  2322. Result := '';
  2323. end; {GetDirName}
  2324. {GetDrive: returns the drive of the Node.}
  2325. function TDriveView.GetDriveToNode(Node: TTreeNode): string;
  2326. var
  2327. Path: string;
  2328. begin
  2329. if (not Assigned (Node)) or (not Assigned(Node.Data)) then
  2330. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDrive']));
  2331. Path := NodePath(Node);
  2332. Result := DriveInfo.GetDriveKey(Path);
  2333. end; {GetDrive}
  2334. {RootNode: returns the rootnode to the Node:}
  2335. function TDriveView.RootNode(Node: TTreeNode): TTreeNode;
  2336. begin
  2337. Result := Node;
  2338. if not Assigned(Node) then
  2339. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['RootNode']));
  2340. while Assigned(Result.Parent) do
  2341. Result := Result.Parent;
  2342. end; {RootNode}
  2343. function TDriveView.GetDisplayName(Node: TTreeNode): string;
  2344. begin
  2345. Result := '';
  2346. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  2347. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDisplayName']));
  2348. if not Assigned(Node.Parent) then Result := GetDriveText(GetDriveToNode(Node))
  2349. else
  2350. begin
  2351. Result := GetDirName(Node);
  2352. end;
  2353. end; {GetDisplayName}
  2354. procedure TDriveView.SetShowVolLabel(ShowIt: Boolean);
  2355. begin
  2356. if ShowIt = FShowVolLabel then
  2357. Exit;
  2358. FShowVolLabel := ShowIt;
  2359. RefreshRootNodes(True);
  2360. end; {SetShowVolLabel}
  2361. procedure TDriveView.DisplayContextMenu(Node: TTreeNode; Point: TPoint);
  2362. var
  2363. Verb: string;
  2364. DirWatched: Boolean;
  2365. begin
  2366. DirWatched := NodeWatched(Node) and WatchThreadActive;
  2367. Assert(Node <> nil);
  2368. if Node <> Selected then
  2369. DropTarget := Node;
  2370. Verb := EmptyStr;
  2371. if Assigned(FOnDisplayContextMenu) then
  2372. FOnDisplayContextMenu(Self);
  2373. ShellDisplayContextMenu(FParentForm.Handle, Point, NodePathName(Node),
  2374. CanEdit(Node), Verb, False);
  2375. if Verb = shcRename then Node.EditText
  2376. else
  2377. if Verb = shcCut then
  2378. begin
  2379. LastClipBoardOperation := cboCut;
  2380. LastPathCut := NodePathName(Node);
  2381. end
  2382. else
  2383. if Verb = shcCopy then LastClipBoardOperation := cboCopy
  2384. else
  2385. if Verb = shcPaste then
  2386. PasteFromClipBoard(NodePathName(Node));
  2387. DropTarget := nil;
  2388. if not DirWatched then
  2389. ValidateDirectory(Node);
  2390. end; {DisplayContextMenu (2)}
  2391. procedure TDriveView.DisplayPropertiesMenu(Node: TTreeNode);
  2392. begin
  2393. Assert(Assigned(Node));
  2394. ShellExecuteContextCommand(FParentForm.Handle, shcProperties, NodePathName(Node));
  2395. end; {ContextMenu}
  2396. procedure TDriveView.SetSelected(Node: TTreeNode);
  2397. begin
  2398. if Node <> Selected then
  2399. begin
  2400. FChangeFlag := False;
  2401. FCanChange := True;
  2402. inherited Selected := Node;
  2403. if not FChangeFlag then
  2404. Change(Selected);
  2405. end;
  2406. end; {SetSelected}
  2407. {Called by TFileDeleteThread, when a file deletion was detected by the D&D receiving application:}
  2408. procedure TDriveView.SignalDirDelete(Sender: TObject; Files: TStringList);
  2409. begin
  2410. if Files.Count > 0 then
  2411. ValidateDirectory(FindNodeToPath(Files[0]));
  2412. end; {SignalDirDelete}
  2413. function TDriveView.DDSourceEffects: TDropEffectSet;
  2414. begin
  2415. if not Assigned(FDragNode.Parent) then
  2416. Result := [deLink]
  2417. else
  2418. Result := [deLink, deCopy, deMove];
  2419. end;
  2420. procedure TDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);
  2421. begin
  2422. if DropTarget = nil then Effect := DROPEFFECT_NONE
  2423. else
  2424. if (KeyState and (MK_CONTROL or MK_SHIFT) = 0) and (PreferredEffect = 0) then
  2425. begin
  2426. if FDragDrive <> '' then
  2427. begin
  2428. if FExeDrag and DriveInfo.IsFixedDrive(GetDriveToNode(DropTarget)) and DriveInfo.IsFixedDrive(FDragDrive) then
  2429. begin
  2430. Effect := DROPEFFECT_LINK;
  2431. end
  2432. else
  2433. if (Effect = DROPEFFECT_COPY) and
  2434. (SameText(FDragDrive, GetDriveToNode(DropTarget)) and
  2435. (FDragDropFilesEx.AvailableDropEffects and DROPEFFECT_MOVE <> 0)) then
  2436. begin
  2437. Effect := DROPEFFECT_MOVE;
  2438. end;
  2439. end;
  2440. end;
  2441. inherited;
  2442. end;
  2443. function TDriveView.DragCompleteFileList: Boolean;
  2444. begin
  2445. Result := (GetDriveTypeToNode(FDragNode) <> DRIVE_REMOVABLE);
  2446. end;
  2447. function TDriveView.DDExecute: TDragResult;
  2448. var
  2449. WatchThreadOK: Boolean;
  2450. DragParentPath: string;
  2451. DragPath: string;
  2452. begin
  2453. WatchThreadOK := WatchThreadActive;
  2454. Result := FDragDropFilesEx.Execute(nil);
  2455. if (Result = drMove) and (not WatchThreadOK) then
  2456. begin
  2457. DragPath := NodePathName(FDragNode);
  2458. if Assigned(FDragNode.Parent) then
  2459. DragParentPath := NodePathName(FDragNode.Parent)
  2460. else
  2461. DragParentPath := DragPath;
  2462. if Assigned(FDragNode.Parent) or (DragParentPath <> NodePathName(Selected.Parent)) then
  2463. begin
  2464. FDragNode := FindNodeToPath(DragPath);
  2465. if Assigned(FDragNode) then
  2466. begin
  2467. FDragFileList.Clear;
  2468. FDragFileList.Add(DragPath);
  2469. TFileDeleteThread.Create(FDragFileList, MaxWaitTimeOut, SignalDirDelete);
  2470. end;
  2471. end;
  2472. end;
  2473. end;
  2474. procedure TDriveView.PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer);
  2475. var
  2476. Index: Integer;
  2477. SourcePath: string;
  2478. SourceParentPath: string;
  2479. SourceIsDirectory: Boolean;
  2480. SaveCursor: TCursor;
  2481. SourceNode, TargetNode: TTreeNode;
  2482. TargetPath: string;
  2483. IsRecycleBin: Boolean;
  2484. begin
  2485. TargetPath := NodePathName(Node);
  2486. IsRecycleBin := NodeIsRecycleBin(Node);
  2487. if FDragDropFilesEx.FileList.Count = 0 then
  2488. Exit;
  2489. SaveCursor := Screen.Cursor;
  2490. Screen.Cursor := crHourGlass;
  2491. SourcePath := EmptyStr;
  2492. try
  2493. if (Effect = DROPEFFECT_COPY) or (Effect = DROPEFFECT_MOVE) then
  2494. begin
  2495. StopAllWatchThreads;
  2496. if Assigned(FDirView) then
  2497. FDirView.StopWatchThread;
  2498. if Assigned(DropSourceControl) and
  2499. (DropSourceControl is TDirView) and
  2500. (DropSourceControl <> FDirView) then
  2501. begin
  2502. TDirView(DropSourceControl).StopWatchThread;
  2503. end;
  2504. if DropFiles(
  2505. DragDropFilesEx, Effect, FFileOperator, TargetPath, false, IsRecycleBin, ConfirmDelete, ConfirmOverwrite, False,
  2506. Self, OnDDFileOperation, SourcePath, SourceIsDirectory) then
  2507. begin
  2508. if Assigned(FOnDDFileOperationExecuted) then
  2509. FOnDDFileOperationExecuted(Self, Effect, SourcePath, TargetPath);
  2510. end;
  2511. ClearDragFileList(FDragDropFilesEx.FileList);
  2512. // TDirView.PerformDragDropFileOperation validates the SourcePath and that actually seems correct
  2513. SourceParentPath := ExtractFilePath(ExcludeTrailingBackslash(SourcePath));
  2514. end
  2515. else
  2516. if Effect = DROPEFFECT_LINK then
  2517. { Create Link requested: }
  2518. begin
  2519. for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do
  2520. begin
  2521. if not DropLink(PFDDListItem(FDragDropFilesEx.FileList[Index]), TargetPath) then
  2522. begin
  2523. DDError(DDCreateShortCutError);
  2524. end;
  2525. end;
  2526. end;
  2527. if Effect = DROPEFFECT_MOVE then
  2528. Items.BeginUpdate;
  2529. {Update source directory, if move-operation was performed:}
  2530. if ((Effect = DROPEFFECT_MOVE) or IsRecycleBin) then
  2531. begin
  2532. // See comment in corresponding operation in TDirView.PerformDragDropFileOperation
  2533. SourceNode := TryFindNodeToPath(SourceParentPath);
  2534. if Assigned(SourceNode) then
  2535. ValidateDirectory(SourceNode);
  2536. end;
  2537. {Update subdirectories of target directory:}
  2538. TargetNode := FindNodeToPath(TargetPath);
  2539. if Assigned(TargetNode) then
  2540. ValidateDirectory(TargetNode)
  2541. else
  2542. ValidateDirectory(GetDriveStatus(DriveInfo.GetDriveKey(TargetPath)).RootNode);
  2543. if Effect = DROPEFFECT_MOVE then
  2544. Items.EndUpdate;
  2545. {Update linked component TDirView:}
  2546. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2547. begin
  2548. case Effect of
  2549. DROPEFFECT_COPY,
  2550. DROPEFFECT_LINK:
  2551. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) then
  2552. FDirView.Reload2;
  2553. DROPEFFECT_MOVE:
  2554. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) or
  2555. (IncludeTrailingBackslash(SourceParentPath) = IncludeTrailingBackslash(DirView.Path)) then
  2556. begin
  2557. if FDirView <> DropSourceControl then FDirView.Reload2;
  2558. end;
  2559. end; {Case}
  2560. end;
  2561. {Update the DropSource control, if files are moved and it is a TDirView:}
  2562. if (Effect = DROPEFFECT_MOVE) and (DropSourceControl is TDirView) then
  2563. begin
  2564. TDirView(DropSourceControl).ValidateSelectedFiles;
  2565. end;
  2566. finally
  2567. FFileOperator.OperandFrom.Clear;
  2568. FFileOperator.OperandTo.Clear;
  2569. StartAllWatchThreads;
  2570. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2571. FDirView.StartWatchThread;
  2572. if Assigned(DropSourceControl) and (DropSourceControl is TDirView) and
  2573. (not TDirView(DropSourceControl).WatchThreadActive) then
  2574. TDirView(DropSourceControl).StartWatchThread;
  2575. Screen.Cursor := SaveCursor;
  2576. end;
  2577. end; {PerformDragDropFileOperation}
  2578. function TDriveView.GetCanUndoCopyMove: Boolean;
  2579. begin
  2580. Result := Assigned(FFileOperator) and FFileOperator.CanUndo;
  2581. end; {CanUndoCopyMove}
  2582. function TDriveView.UndoCopyMove: Boolean;
  2583. var
  2584. LastTarget: string;
  2585. LastSource: string;
  2586. begin
  2587. Result := False;
  2588. if FFileOperator.CanUndo then
  2589. begin
  2590. Lasttarget := FFileOperator.LastOperandTo[0];
  2591. LastSource := FFileOperator.LastOperandFrom[0];
  2592. StopAllWatchThreads;
  2593. Result := FFileOperator.UndoExecute;
  2594. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastTarget)));
  2595. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastSource)));
  2596. StartAllWatchThreads;
  2597. if Assigned(FDirView) then
  2598. with FDirView do
  2599. if not WatchThreadActive then
  2600. begin
  2601. if (IncludeTrailingBackslash(ExtractFilePath(LastTarget)) = IncludeTrailingBackslash(Path)) or
  2602. (IncludeTrailingBackslash(ExtractFilePath(LastSource)) = IncludeTrailingBackslash(Path)) then
  2603. Reload2;
  2604. end;
  2605. end;
  2606. end; {UndoCopyMove}
  2607. {Clipboard operations:}
  2608. procedure TDriveView.SetLastPathCut(Path: string);
  2609. var
  2610. Node: TTreeNode;
  2611. begin
  2612. if FLastPathCut <> Path then
  2613. begin
  2614. Node := FindNodeToPath(FLastPathCut);
  2615. if Assigned(Node) then
  2616. begin
  2617. FLastPathCut := Path;
  2618. Node.Cut := False;
  2619. end;
  2620. Node := FindNodeToPath(Path);
  2621. if Assigned(Node) then
  2622. begin
  2623. FLastPathCut := Path;
  2624. Node.Cut := True;
  2625. end;
  2626. end;
  2627. end; {SetLastNodeCut}
  2628. procedure TDriveView.EmptyClipboard;
  2629. begin
  2630. if Windows.OpenClipBoard(0) then
  2631. begin
  2632. Windows.EmptyClipBoard;
  2633. Windows.CloseClipBoard;
  2634. LastPathCut := '';
  2635. LastClipBoardOperation := cboNone;
  2636. if Assigned(FDirView) then
  2637. FDirView.EmptyClipboard;
  2638. end;
  2639. end; {EmptyClipBoard}
  2640. function TDriveView.CopyToClipBoard(Node: TTreeNode): Boolean;
  2641. begin
  2642. Result := Assigned(Selected);
  2643. if Result then
  2644. begin
  2645. EmptyClipBoard;
  2646. ClearDragFileList(FDragDropFilesEx.FileList);
  2647. AddToDragFileList(FDragDropFilesEx.FileList, Selected);
  2648. Result := FDragDropFilesEx.CopyToClipBoard;
  2649. LastClipBoardOperation := cboCopy;
  2650. end;
  2651. end; {CopyToClipBoard}
  2652. function TDriveView.CutToClipBoard(Node: TTreeNode): Boolean;
  2653. begin
  2654. Result := Assigned(Node) and Assigned(Node.Parent) and CopyToClipBoard(Node);
  2655. if Result then
  2656. begin
  2657. LastPathCut := NodePathName(Node);
  2658. LastClipBoardOperation := cboCut;
  2659. end;
  2660. end; {CutToClipBoard}
  2661. function TDriveView.CanPasteFromClipBoard: Boolean;
  2662. begin
  2663. Result := False;
  2664. if Assigned(Selected) and Windows.OpenClipboard(0) then
  2665. begin
  2666. Result := IsClipboardFormatAvailable(CF_HDROP);
  2667. Windows.CloseClipBoard;
  2668. end;
  2669. end; {CanPasteFromClipBoard}
  2670. function TDriveView.PasteFromClipBoard(TargetPath: String = ''): Boolean;
  2671. begin
  2672. ClearDragFileList(FDragDropFilesEx.FileList);
  2673. Result := False;
  2674. if CanPasteFromClipBoard and {MP}FDragDropFilesEx.GetFromClipBoard{/MP}
  2675. then
  2676. begin
  2677. if TargetPath = '' then
  2678. TargetPath := NodePathName(Selected);
  2679. case LastClipBoardOperation of
  2680. cboCopy,
  2681. cboNone:
  2682. begin
  2683. PerformDragDropFileOperation(Selected, DROPEFFECT_COPY);
  2684. if Assigned(FOnDDExecuted) then
  2685. FOnDDExecuted(Self, DROPEFFECT_COPY);
  2686. end;
  2687. cboCut:
  2688. begin
  2689. PerformDragDropFileOperation(Selected, DROPEFFECT_MOVE);
  2690. if Assigned(FOnDDExecuted) then
  2691. FOnDDExecuted(Self, DROPEFFECT_MOVE);
  2692. EmptyClipBoard;
  2693. end;
  2694. end;
  2695. Result := True;
  2696. end;
  2697. end; {PasteFromClipBoard}
  2698. procedure TDriveView.CMSysColorChange(var Message: TMessage);
  2699. begin
  2700. if not FValidateFlag then
  2701. begin
  2702. inherited;
  2703. end
  2704. else
  2705. begin
  2706. // Do not recreate the handle, if we are just iterating nodes, at that invalidates the node objects.
  2707. // This is not perfect, as the handle can be recreated for other reasons.
  2708. // But system color change is by far the most common case.
  2709. FSysColorChangePending := True;
  2710. end;
  2711. end;
  2712. end.