1
0

DriveView.pas 87 KB

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