DriveView.pas 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151
  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. procedure ReadSubDirs(Node: TTreeNode);
  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. Assert(False);
  694. inherited Assign(Source);
  695. if not Deleting and (Source is TTreeNode) then
  696. begin
  697. SourceData := TNodeData(TTreeNode(Source).Data);
  698. NewData := TNodeData.Create();
  699. NewData.DirName := SourceData.DirName;
  700. NewData.Attr := SourceData.Attr;
  701. NewData.Scanned := SourceData.Scanned;
  702. NewData.Data := SourceData.Data;
  703. NewData.FIsRecycleBin := SourceData.FIsRecycleBin;
  704. NewData.IconEmpty := SourceData.IconEmpty;
  705. TTreeNode(Source).Data := NewData;
  706. end;
  707. end;
  708. { TDriveView }
  709. constructor TDriveView.Create(AOwner: TComponent);
  710. var
  711. Drive: TRealDrive;
  712. ChangeNotifyEntry: TSHChangeNotifyEntry;
  713. Dummy: string;
  714. begin
  715. inherited;
  716. FCreating := True;
  717. FDriveStatus := TObjectDictionary<string, TDriveStatus>.Create([doOwnsValues]);
  718. FChangeInterval := MSecsPerSec;
  719. for Drive := FirstDrive to LastDrive do
  720. begin
  721. FDriveStatus.Add(Drive, CreateDriveStatus);
  722. end;
  723. FFileOperator := TFileOperator.Create(Self);
  724. FSubDirReaderThread := TSubDirReaderThread.Create(Self);
  725. FShowVolLabel := True;
  726. FChangeFlag := False;
  727. FLastDir := EmptyStr;
  728. FValidateFlag := False;
  729. FConfirmDelete := True;
  730. FDirectory := EmptyStr;
  731. FForceRename := False;
  732. FLastRenameName := '';
  733. FRenameNode := nil;
  734. FPrevSelected := nil;
  735. FPrevSelectedIndex := -1;
  736. FChangeTimerSuspended := 0;
  737. FConfirmOverwrite := True;
  738. FLastPathCut := '';
  739. FStartPos.X := -1;
  740. FStartPos.Y := -1;
  741. FDragPos := FStartPos;
  742. FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
  743. // Source: petr.solin 2022-02-25
  744. FChangeNotify := 0;
  745. if SpecialFolderLocation(CSIDL_DESKTOP, Dummy, ChangeNotifyEntry.pidl) then
  746. begin
  747. ChangeNotifyEntry.fRecursive := False;
  748. FChangeNotify :=
  749. SHChangeNotifyRegister(
  750. FInternalWindowHandle, SHCNRF_ShellLevel or SHCNRF_NewDelivery,
  751. SHCNE_RENAMEFOLDER or SHCNE_MEDIAINSERTED or SHCNE_MEDIAREMOVED,
  752. WM_USER_SHCHANGENOTIFY, 1, ChangeNotifyEntry);
  753. end;
  754. with FDragDropFilesEx do
  755. begin
  756. ShellExtensions.DragDropHandler := True;
  757. end;
  758. end; {Create}
  759. destructor TDriveView.Destroy;
  760. var
  761. DriveStatusPair: TDriveStatusPair;
  762. begin
  763. if FChangeNotify <> 0 then SHChangeNotifyDeregister(FChangeNotify);
  764. Classes.DeallocateHWnd(FInternalWindowHandle);
  765. for DriveStatusPair in FDriveStatus do
  766. begin
  767. with DriveStatusPair.Value do
  768. begin
  769. if Assigned(DiscMonitor) then
  770. FreeAndNil(DiscMonitor);
  771. if Assigned(ChangeTimer) then
  772. FreeAndNil(ChangeTimer);
  773. end;
  774. UpdateDriveNotifications(DriveStatusPair.Key);
  775. end;
  776. FDriveStatus.Free;
  777. if Assigned(FFileOperator) then
  778. FFileOperator.Free;
  779. FSubDirReaderThread.Free;
  780. inherited Destroy;
  781. end; {Destroy}
  782. function TDriveView.CreateDriveStatus: TDriveStatus;
  783. begin
  784. Result := TDriveStatus.Create;
  785. with Result do
  786. begin
  787. Scanned := False;
  788. Verified := False;
  789. RootNode := nil;
  790. RootNodeIndex := -1;
  791. DiscMonitor := nil;
  792. DefaultDir := EmptyStr;
  793. {ChangeTimer: }
  794. ChangeTimer := TTimer.Create(Self);
  795. ChangeTimer.Interval := 0;
  796. ChangeTimer.Enabled := False;
  797. ChangeTimer.OnTimer := ChangeTimerOnTimer;
  798. DriveHandle := INVALID_HANDLE_VALUE;
  799. NotificationHandle := nil;
  800. end;
  801. end;
  802. procedure TDriveView.DriveRemoving(Drive: string);
  803. begin
  804. DriveRemoved(Drive);
  805. TerminateWatchThread(Drive);
  806. end;
  807. type
  808. PDevBroadcastHdr = ^TDevBroadcastHdr;
  809. TDevBroadcastHdr = record
  810. dbch_size: DWORD;
  811. dbch_devicetype: DWORD;
  812. dbch_reserved: DWORD;
  813. end;
  814. PDevBroadcastVolume = ^TDevBroadcastVolume;
  815. TDevBroadcastVolume = record
  816. dbcv_size: DWORD;
  817. dbcv_devicetype: DWORD;
  818. dbcv_reserved: DWORD;
  819. dbcv_unitmask: DWORD;
  820. dbcv_flags: WORD;
  821. end;
  822. PDEV_BROADCAST_HANDLE = ^DEV_BROADCAST_HANDLE;
  823. DEV_BROADCAST_HANDLE = record
  824. dbch_size : DWORD;
  825. dbch_devicetype : DWORD;
  826. dbch_reserved : DWORD;
  827. dbch_handle : THandle;
  828. dbch_hdevnotify : HDEVNOTIFY ;
  829. dbch_eventguid : TGUID;
  830. dbch_nameoffset : LongInt;
  831. dbch_data : Byte;
  832. end;
  833. PPItemIDList = ^PItemIDList;
  834. const
  835. DBT_DEVTYP_HANDLE = $00000006;
  836. DBT_CONFIGCHANGED = $0018;
  837. DBT_DEVICEARRIVAL = $8000;
  838. DBT_DEVICEQUERYREMOVE = $8001;
  839. DBT_DEVICEREMOVEPENDING = $8003;
  840. DBT_DEVICEREMOVECOMPLETE = $8004;
  841. DBT_DEVTYP_VOLUME = $00000002;
  842. // WORKAROUND Declaration in Winapi.ShlObj.pas is wrong
  843. function SHChangeNotification_Lock(hChange: THandle; dwProcId: DWORD;
  844. var PPidls: PPItemIDList; var plEvent: Longint): THANDLE; stdcall;
  845. external 'shell32.dll' name 'SHChangeNotification_Lock';
  846. procedure TDriveView.InternalWndProc(var Msg: TMessage);
  847. var
  848. DeviceType: DWORD;
  849. UnitMask: DWORD;
  850. DeviceHandle: THandle;
  851. Drive: Char;
  852. DriveStatusPair: TDriveStatusPair;
  853. PPIDL: PPItemIDList;
  854. Event: LONG;
  855. Lock: THandle;
  856. begin
  857. with Msg do
  858. begin
  859. if Msg = WM_USER_SHCHANGENOTIFY then
  860. begin
  861. Lock := SHChangeNotification_Lock(wParam, lParam, PPIDL, Event);
  862. try
  863. if (Event = SHCNE_RENAMEFOLDER) or // = drive rename
  864. (Event = SHCNE_MEDIAINSERTED) or // also bitlocker drive unlock (also sends SHCNE_UPDATEDIR)
  865. (Event = SHCNE_MEDIAREMOVED) then
  866. begin
  867. ScheduleDriveRefresh;
  868. end;
  869. finally
  870. SHChangeNotification_Unlock(Lock);
  871. end;
  872. end
  873. else
  874. if Msg = WM_DEVICECHANGE then
  875. begin
  876. if (wParam = DBT_CONFIGCHANGED) or
  877. (wParam = DBT_DEVICEARRIVAL) or
  878. (wParam = DBT_DEVICEREMOVECOMPLETE) then
  879. begin
  880. ScheduleDriveRefresh;
  881. end
  882. else
  883. if (wParam = DBT_DEVICEQUERYREMOVE) or
  884. (wParam = DBT_DEVICEREMOVEPENDING) then
  885. begin
  886. DeviceType := PDevBroadcastHdr(lParam)^.dbch_devicetype;
  887. // This is specifically for VeraCrypt.
  888. // For normal drives, see DBT_DEVTYP_HANDLE below
  889. // (and maybe now that we have generic implementation, this specific code for VeraCrypt might not be needed anymore)
  890. if DeviceType = DBT_DEVTYP_VOLUME then
  891. begin
  892. UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
  893. Drive := FirstDrive;
  894. while UnitMask > 0 do
  895. begin
  896. if UnitMask and $01 <> 0 then
  897. begin
  898. DriveRemoving(Drive);
  899. end;
  900. UnitMask := UnitMask shr 1;
  901. Drive := Chr(Ord(Drive) + 1);
  902. end;
  903. end
  904. else
  905. if DeviceType = DBT_DEVTYP_HANDLE then
  906. begin
  907. DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
  908. for DriveStatusPair in FDriveStatus do
  909. if DriveStatusPair.Value.DriveHandle = DeviceHandle then
  910. begin
  911. DriveRemoving(DriveStatusPair.Key);
  912. end;
  913. end;
  914. end;
  915. end
  916. else
  917. if Msg = WM_TIMER then
  918. begin
  919. CancelDriveRefresh;
  920. try
  921. //DriveInfo.Load;
  922. RefreshRootNodes(dsAll or dvdsRereadAllways);
  923. DoRefreshDrives(True);
  924. except
  925. Application.HandleException(Self);
  926. end;
  927. end;
  928. Result := DefWindowProc(FInternalWindowHandle, Msg, wParam, lParam);
  929. end;
  930. end;
  931. procedure TDriveView.DoRefreshDrives(Global: Boolean);
  932. begin
  933. if Assigned(OnRefreshDrives) then
  934. OnRefreshDrives(Self, Global);
  935. end;
  936. procedure TDriveView.CancelDriveRefresh;
  937. begin
  938. KillTimer(FInternalWindowHandle, 1);
  939. end;
  940. procedure TDriveView.ScheduleDriveRefresh;
  941. begin
  942. CancelDriveRefresh;
  943. // Delay refreshing drives for a sec.
  944. // Particularly with CD/DVD drives, if we query display name
  945. // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
  946. // Actually one sec does not help usually, but we do not want to wait any longer,
  947. // because we want to add USB drives asap.
  948. // And this problem might be solved now by SHChangeNotifyRegister/SHCNE_RENAMEFOLDER.
  949. SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
  950. end;
  951. procedure TDriveView.CreateWnd;
  952. var
  953. DriveStatus: TDriveStatus;
  954. begin
  955. inherited;
  956. if Assigned(PopupMenu) then
  957. PopupMenu.Autopopup := False;
  958. FDragDropFilesEx.SourceEffects := [deCopy, deMove, deLink];
  959. FDragDropFilesEx.TargetEffects := [deCopy, deMove, deLink];
  960. if FPrevSelectedIndex >= 0 then
  961. begin
  962. FPrevSelected := Items[FPrevSelectedIndex];
  963. FPrevSelectedIndex := -1;
  964. end;
  965. for DriveStatus in FDriveStatus.Values do
  966. with DriveStatus do
  967. begin
  968. if RootNodeIndex >= 0 then
  969. begin
  970. RootNode := Items[RootNodeIndex];
  971. RootNodeIndex := -1;
  972. end;
  973. end;
  974. end; {CreateWnd}
  975. procedure TDriveView.DestroyWnd;
  976. var
  977. DriveStatus: TDriveStatus;
  978. begin
  979. if not (csRecreating in ControlState) then
  980. begin
  981. FSubDirReaderThread.Terminate;
  982. FSubDirReaderThread.WaitFor;
  983. end
  984. else
  985. if CreateWndRestores and (Items.Count > 0) then
  986. begin
  987. FPrevSelectedIndex := -1;
  988. if Assigned(FPrevSelected) then
  989. begin
  990. FPrevSelectedIndex := FPrevSelected.AbsoluteIndex;
  991. FPrevSelected := nil;
  992. end;
  993. for DriveStatus in FDriveStatus.Values do
  994. with DriveStatus do
  995. begin
  996. RootNodeIndex := -1;
  997. if Assigned(RootNode) then
  998. begin
  999. RootNodeIndex := RootNode.AbsoluteIndex;
  1000. RootNode := nil;
  1001. end;
  1002. end;
  1003. end;
  1004. inherited;
  1005. end;
  1006. function TDriveView.NodeColor(Node: TTreeNode): TColor;
  1007. begin
  1008. Result := clDefaultItemColor;
  1009. with TNodeData(Node.Data) do
  1010. if not Node.Selected then
  1011. begin
  1012. {Colored display of compressed directories:}
  1013. if (Attr and FILE_ATTRIBUTE_COMPRESSED) <> 0 then
  1014. begin
  1015. if SupportsDarkMode and DarkMode then Result := clSkyBlue
  1016. else Result := clBlue;
  1017. end
  1018. else
  1019. {Dimmed display, if hidden-atrribut set:}
  1020. if FDimmHiddenDirs and ((Attr and FILE_ATTRIBUTE_HIDDEN) <> 0) then
  1021. Result := clGrayText
  1022. end;
  1023. end;
  1024. function TDriveView.GetCustomDirView: TCustomDirView;
  1025. begin
  1026. Result := DirView;
  1027. end;
  1028. procedure TDriveView.SetCustomDirView(Value: TCustomDirView);
  1029. begin
  1030. DirView := Value as TDirView;
  1031. end;
  1032. function TDriveView.NodePath(Node: TTreeNode): string;
  1033. var
  1034. ParentNode: TTreeNode;
  1035. begin
  1036. if not Assigned(Node) then
  1037. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDirPath']));
  1038. Result := GetDirName(Node);
  1039. ParentNode := Node.Parent;
  1040. while (ParentNode <> nil) and (ParentNode.Level >= 0) do
  1041. begin
  1042. if ParentNode.Level > 0 then
  1043. Result := GetDirName(ParentNode) + '\' + Result
  1044. else
  1045. Result := GetDirName(ParentNode) + Result;
  1046. ParentNode := ParentNode.Parent;
  1047. end;
  1048. if IsRootPath(Result) then
  1049. Result := ExcludeTrailingBackslash(Result);
  1050. end;
  1051. {NodePathName: Returns the complete path to Node with trailing backslash on rootnodes:
  1052. C:\ ,C:\WINDOWS, C:\WINDOWS\SYSTEM }
  1053. function TDriveView.NodePathName(Node: TTreeNode): string;
  1054. begin
  1055. Result := NodePath(Node);
  1056. if IsRootPath(Result) then
  1057. Result := IncludeTrailingBackslash(Result);
  1058. end; {NodePathName}
  1059. function TDriveView.NodeIsRecycleBin(Node: TTreeNode): Boolean;
  1060. begin
  1061. Result := TNodeData(Node.Data).IsRecycleBin;
  1062. end;
  1063. function TDriveView.NodePathExists(Node: TTreeNode): Boolean;
  1064. begin
  1065. Result := DirectoryExists(NodePathName(Node));
  1066. end;
  1067. function TDriveView.CanEdit(Node: TTreeNode): Boolean;
  1068. begin
  1069. Result := inherited CanEdit(Node) or FForceRename;
  1070. if Result then
  1071. begin
  1072. Result := Assigned(Node.Parent) and
  1073. (not TNodeData(Node.Data).IsRecycleBin) and
  1074. (not ReadOnly) and
  1075. (FDragDropFilesEx.DragDetectStatus <> ddsDrag) and
  1076. ((TNodeData(Node.Data).Attr and (faReadOnly or faSysFile)) = 0) and
  1077. (UpperCase(Node.Text) = UpperCase(GetDirName(Node)));
  1078. end;
  1079. FForceRename := False;
  1080. end; {CanEdit}
  1081. procedure TDriveView.Edit(const Item: TTVItem);
  1082. var
  1083. Node: TTreeNode;
  1084. Info: string;
  1085. i: Integer;
  1086. begin
  1087. Node := GetNodeFromHItem(Item);
  1088. if (Length(Item.pszText) > 0) and (Item.pszText <> Node.Text) then
  1089. begin
  1090. if StrContains(coInvalidDosChars, Item.pszText) then
  1091. begin
  1092. Info := coInvalidDosChars;
  1093. for i := Length(Info) downto 1 do
  1094. System.Insert(Space, Info, i);
  1095. if Length(Item.pszText) > 0 then
  1096. raise EInvalidDirName.Create(SErrorInvalidName + Space + Info);
  1097. Exit;
  1098. end;
  1099. StopWatchThread;
  1100. if Assigned(DirView) then
  1101. DirView.StopWatchThread;
  1102. with FFileOperator do
  1103. begin
  1104. Flags := FileOperatorDefaultFlags + [foNoConfirmation];
  1105. Operation := foRename;
  1106. OperandFrom.Clear;
  1107. OperandTo.Clear;
  1108. OperandFrom.Add(NodePath(Node));
  1109. OperandTo.Add(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText);
  1110. end;
  1111. try
  1112. if FFileOperator.Execute then
  1113. begin
  1114. Node.Text := Item.pszText;
  1115. TNodeData(Node.Data).DirName := Item.pszText;
  1116. SortChildren(Node.Parent, False);
  1117. inherited;
  1118. end
  1119. else
  1120. begin
  1121. if FileOrDirExists(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText) then
  1122. Info := SErrorRenameFileExists + Item.pszText
  1123. else
  1124. Info := SErrorRenameFile + Item.pszText;
  1125. MessageBeep(MB_ICONHAND);
  1126. if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then
  1127. begin
  1128. FLastRenameName := Item.pszText;
  1129. FRenameNode := Node;
  1130. PostMessage(Self.Handle, WM_USER_RENAME, 0, 0);
  1131. end;
  1132. end;
  1133. finally
  1134. StartWatchThread;
  1135. if Assigned(DirView) then
  1136. begin
  1137. DirView.Reload2;
  1138. DirView.StartWatchThread;
  1139. end;
  1140. end;
  1141. end;
  1142. end; {Edit}
  1143. procedure TDriveView.WMUserRename(var Message: TMessage);
  1144. begin
  1145. if Assigned(FRenameNode) then
  1146. begin
  1147. FForceRename := True;
  1148. TreeView_EditLabel(Handle, FRenameNode.ItemID);
  1149. SetWindowText(TreeView_GetEditControl(Self.Handle), PChar(FLastRenameName));
  1150. FRenameNode := nil;
  1151. end;
  1152. end; {WMUserRename}
  1153. function TDriveView.CanExpand(Node: TTreeNode): Boolean;
  1154. var
  1155. SubNode: TTreeNode;
  1156. Drive: string;
  1157. SaveCursor: TCursor;
  1158. begin
  1159. Result := inherited CanExpand(Node);
  1160. Drive := GetDriveToNode(Node);
  1161. if Node.HasChildren then
  1162. begin
  1163. if (Node.Level = 0) and
  1164. (not GetDriveStatus(Drive).Scanned) and
  1165. DriveInfo.IsFixedDrive(Drive) then
  1166. begin
  1167. SubNode := Node.GetFirstChild;
  1168. if not Assigned(SubNode) then
  1169. begin
  1170. ScanDrive(Drive);
  1171. SubNode := Node.GetFirstChild;
  1172. Node.HasChildren := Assigned(SubNode);
  1173. Result := Node.HasChildren;
  1174. if not Assigned(GetDriveStatus(Drive).DiscMonitor) then
  1175. CreateWatchThread(Drive);
  1176. end;
  1177. end
  1178. else
  1179. begin
  1180. SaveCursor := Screen.Cursor;
  1181. Screen.Cursor := crHourGlass;
  1182. try
  1183. if (not TNodeData(Node.Data).Scanned) and DoScanDir(Node) then
  1184. begin
  1185. ReadSubDirs(Node);
  1186. end;
  1187. finally
  1188. Screen.Cursor := SaveCursor;
  1189. end;
  1190. end;
  1191. end;
  1192. end; {CanExpand}
  1193. procedure TDriveView.GetImageIndex(Node: TTreeNode);
  1194. begin
  1195. if TNodeData(Node.Data).IconEmpty then
  1196. SetImageIndex(Node);
  1197. inherited;
  1198. end; {GetImageIndex}
  1199. procedure TDriveView.Loaded;
  1200. begin
  1201. inherited;
  1202. {Create the drive nodes:}
  1203. RefreshRootNodes(dsDisplayName or dvdsFloppy);
  1204. {Set the initial directory:}
  1205. if (Length(FDirectory) > 0) and DirectoryExists(FDirectory) then
  1206. Directory := FDirectory;
  1207. FCreating := False;
  1208. end; {Loaded}
  1209. function TDriveView.CreateNode: TTreeNode;
  1210. begin
  1211. Result := TDriveTreeNode.Create(Items);
  1212. end;
  1213. procedure TDriveView.Delete(Node: TTreeNode);
  1214. var
  1215. NodeData: TNodeData;
  1216. begin
  1217. if Node = FPrevSelected then
  1218. FPrevSelected := nil;
  1219. NodeData := nil;
  1220. if Assigned(Node) and Assigned(Node.Data) then
  1221. NodeData := TNodeData(Node.Data);
  1222. Node.Data := nil;
  1223. inherited;
  1224. if Assigned(NodeData) and not (csRecreating in ControlState) then
  1225. begin
  1226. FSubDirReaderThread.Delete(Node);
  1227. NodeData.Destroy;
  1228. end;
  1229. end; {OnDelete}
  1230. procedure TDriveView.KeyPress(var Key: Char);
  1231. begin
  1232. inherited;
  1233. if Assigned(Selected) then
  1234. begin
  1235. if Pos(Key, coInvalidDosChars) <> 0 then
  1236. begin
  1237. Beep;
  1238. Key := #0;
  1239. end;
  1240. end;
  1241. end; {KeyPress}
  1242. function TDriveView.CanChange(Node: TTreeNode): Boolean;
  1243. var
  1244. Path: string;
  1245. Drive: string;
  1246. begin
  1247. Result := inherited CanChange(Node);
  1248. if not Reading and not (csRecreating in ControlState) then
  1249. begin
  1250. if Result and Assigned(Node) then
  1251. begin
  1252. Path := NodePathName(Node);
  1253. if Path <> FLastDir then
  1254. begin
  1255. Drive := DriveInfo.GetDriveKey(Path);
  1256. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  1257. if not DriveInfo.Get(Drive).DriveReady then
  1258. begin
  1259. MessageDlg(Format(SDriveNotReady, [Drive]), mtError, [mbOK], 0);
  1260. Result := False;
  1261. end
  1262. else
  1263. try
  1264. CheckCanOpenDirectory(Path);
  1265. except
  1266. Application.HandleException(Self);
  1267. Result := False;
  1268. end;
  1269. end;
  1270. end;
  1271. if Result and (csDestroying in ComponentState) then
  1272. begin
  1273. Result := False;
  1274. end;
  1275. if Result and
  1276. (not FCanChange) and
  1277. Assigned(Node) and
  1278. Assigned(Node.Data) and
  1279. Assigned(Selected) and
  1280. Assigned(Selected.Data) then
  1281. begin
  1282. DropTarget := Node;
  1283. Result := False;
  1284. end
  1285. else
  1286. begin
  1287. DropTarget := nil;
  1288. end;
  1289. end;
  1290. end; {CanChange}
  1291. procedure TDriveView.Change(Node: TTreeNode);
  1292. var
  1293. Drive: string;
  1294. OldSerial: DWORD;
  1295. NewDir: string;
  1296. PrevDrive: string;
  1297. begin
  1298. if not Reading and not (csRecreating in ControlState) then
  1299. begin
  1300. if Assigned(Node) then
  1301. begin
  1302. NewDir := NodePathName(Node);
  1303. if NewDir <> FLastDir then
  1304. begin
  1305. Drive := DriveInfo.GetDriveKey(NewDir);
  1306. if Length(FLastDir) > 0 then
  1307. PrevDrive := DriveInfo.GetDriveKey(FLastDir)
  1308. else
  1309. PrevDrive := '';
  1310. FChangeFlag := True;
  1311. FLastDir := NewDir;
  1312. OldSerial := DriveInfo.Get(Drive).DriveSerial;
  1313. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  1314. with DriveInfo.Get(Drive) do
  1315. begin
  1316. if Assigned(FDirView) and (FDirView.Path <> NewDir) then
  1317. FDirView.Path := NewDir;
  1318. if DriveReady then
  1319. begin
  1320. if not DirectoryExists(NewDir) then
  1321. begin
  1322. ValidateDirectory(GetDriveStatus(Drive).RootNode);
  1323. Exit;
  1324. end;
  1325. GetDriveStatus(Drive).DefaultDir := IncludeTrailingBackslash(NewDir);
  1326. if PrevDrive <> Drive then
  1327. begin
  1328. if (PrevDrive <> '') and
  1329. (DriveInfo.Get(PrevDrive).DriveType = DRIVE_REMOVABLE) then
  1330. begin
  1331. TerminateWatchThread(PrevDrive);
  1332. end;
  1333. {Drive serial has changed or is missing: allways reread the drive:}
  1334. if (DriveSerial <> OldSerial) or (DriveSerial = 0) then
  1335. begin
  1336. if TNodeData(GetDriveStatus(Drive).RootNode.Data).Scanned then
  1337. ScanDrive(Drive);
  1338. end;
  1339. end;
  1340. StartWatchThread;
  1341. end
  1342. else {Drive not ready:}
  1343. begin
  1344. GetDriveStatus(Drive).RootNode.DeleteChildren;
  1345. GetDriveStatus(Drive).DefaultDir := EmptyStr;
  1346. end;
  1347. end;
  1348. end;
  1349. if (not Assigned(FPrevSelected)) or (not FPrevSelected.HasAsParent(Node)) then
  1350. Node.Expand(False);
  1351. FPrevSelected := Node;
  1352. ValidateCurrentDirectoryIfNotMonitoring;
  1353. end;
  1354. end;
  1355. inherited;
  1356. end; {Change}
  1357. procedure TDriveView.SetImageIndex(Node: TTreeNode);
  1358. var
  1359. FileInfo: TShFileInfo;
  1360. Drive, NodePath: string;
  1361. begin
  1362. if Assigned(Node) and TNodeData(Node.Data).IconEmpty then
  1363. begin
  1364. NodePath := NodePathName(Node);
  1365. Drive := DriveInfo.GetDriveKey(NodePath);
  1366. if Node.Level = 0 then
  1367. begin
  1368. with DriveInfo.Get(Drive) do
  1369. begin
  1370. if ImageIndex = 0 then
  1371. begin
  1372. DriveInfo.ReadDriveStatus(Drive, dsImageIndex);
  1373. Node.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1374. end
  1375. else Node.ImageIndex := ImageIndex;
  1376. Node.SelectedIndex := Node.ImageIndex;
  1377. end;
  1378. end
  1379. else
  1380. begin
  1381. if DriveInfo.Get(Drive).DriveType = DRIVE_REMOTE then
  1382. begin
  1383. Node.ImageIndex := StdDirIcon;
  1384. Node.SelectedIndex := StdDirSelIcon;
  1385. end
  1386. else
  1387. begin
  1388. try
  1389. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1390. SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  1391. if (FileInfo.iIcon < Images.Count) and (FileInfo.iIcon > 0) then
  1392. begin
  1393. Node.ImageIndex := FileInfo.iIcon;
  1394. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1395. SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_OPENICON);
  1396. Node.SelectedIndex := FileInfo.iIcon;
  1397. end
  1398. else
  1399. begin
  1400. Node.ImageIndex := StdDirIcon;
  1401. Node.SelectedIndex := StdDirSelIcon;
  1402. end;
  1403. except
  1404. Node.ImageIndex := StdDirIcon;
  1405. Node.SelectedIndex := StdDirSelIcon;
  1406. end;
  1407. end;
  1408. end;
  1409. end; {IconEmpty}
  1410. TNodeData(Node.Data).IconEmpty := False;
  1411. end; {SetImageIndex}
  1412. function TDriveView.GetDriveText(Drive: string): string;
  1413. begin
  1414. if FShowVolLabel and (Length(DriveInfo.GetPrettyName(Drive)) > 0) then
  1415. begin
  1416. case FVolDisplayStyle of
  1417. doPrettyName: Result := DriveInfo.GetPrettyName(Drive);
  1418. doDisplayName: Result := DriveInfo.GetDisplayName(Drive);
  1419. end; {Case}
  1420. end
  1421. else
  1422. begin
  1423. Result := DriveInfo.GetSimpleName(Drive);
  1424. end;
  1425. end; {GetDriveText}
  1426. function CompareDrive(List: TStringList; Index1, Index2: Integer): Integer;
  1427. var
  1428. Drive1, Drive2: string;
  1429. RealDrive1, RealDrive2: Boolean;
  1430. begin
  1431. Drive1 := List[Index1];
  1432. Drive2 := List[Index2];
  1433. RealDrive1 := DriveInfo.IsRealDrive(Drive1);
  1434. RealDrive2 := DriveInfo.IsRealDrive(Drive2);
  1435. if RealDrive1 = RealDrive2 then
  1436. begin
  1437. Result := CompareText(Drive1, Drive2);
  1438. end
  1439. else
  1440. if RealDrive1 and (not RealDrive2) then
  1441. begin
  1442. Result := -1;
  1443. end
  1444. else
  1445. begin
  1446. Result := 1;
  1447. end;
  1448. end;
  1449. function TDriveView.GetDrives: TStrings;
  1450. var
  1451. DriveStatusPair: TDriveStatusPair;
  1452. Drives: TStringList;
  1453. begin
  1454. Drives := TStringList.Create;
  1455. { We could iterate only .Keys here, but that crashes IDE for some reason }
  1456. for DriveStatusPair in FDriveStatus do
  1457. begin
  1458. Drives.Add(DriveStatusPair.Key);
  1459. end;
  1460. Drives.CustomSort(CompareDrive);
  1461. Result := Drives;
  1462. end;
  1463. procedure TDriveView.DriveRemoved(Drive: string);
  1464. var
  1465. NewDrive: Char;
  1466. begin
  1467. if (Directory <> '') and (Directory[1] = Drive) then
  1468. begin
  1469. if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
  1470. else NewDrive := SystemDrive;
  1471. repeat
  1472. if NewDrive < SystemDrive then NewDrive := SystemDrive
  1473. else
  1474. if NewDrive = SystemDrive then NewDrive := LastDrive
  1475. else Dec(NewDrive);
  1476. DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
  1477. if NewDrive = Drive then
  1478. begin
  1479. Break;
  1480. end;
  1481. if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
  1482. begin
  1483. Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
  1484. break;
  1485. end;
  1486. until False;
  1487. if not Assigned(Selected) then
  1488. begin
  1489. Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
  1490. end;
  1491. end;
  1492. end;
  1493. procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
  1494. var
  1495. Drives: TStrings;
  1496. NewText: string;
  1497. SaveCursor: TCursor;
  1498. WasValid: Boolean;
  1499. NodeData: TNodeData;
  1500. DriveStatus: TDriveStatus;
  1501. NextDriveNode: TTreeNode;
  1502. Index: Integer;
  1503. Drive: string;
  1504. begin
  1505. SaveCursor := Screen.Cursor;
  1506. Screen.Cursor := crHourGlass;
  1507. Drives := nil;
  1508. try
  1509. Drives := GetDrives;
  1510. NextDriveNode := nil;
  1511. for Index := Drives.Count - 1 downto 0 do
  1512. begin
  1513. Drive := Drives[Index];
  1514. DriveStatus := GetDriveStatus(Drive);
  1515. if ((dsFlags and dvdsFloppy) <> 0) or DriveInfo.IsFixedDrive(Drive) then
  1516. begin
  1517. with DriveInfo.Get(Drive) do
  1518. begin
  1519. WasValid := Assigned(DriveStatus.RootNode);
  1520. end;
  1521. if ((dsFlags and dvdsReReadAllways) = 0) and
  1522. (Length(DriveInfo.Get(Drive).DisplayName) > 0) then
  1523. dsFlags := dsFlags and (not dsDisplayName);
  1524. DriveInfo.ReadDriveStatus(Drive, dsFlags);
  1525. with DriveInfo.Get(Drive), DriveStatus do
  1526. begin
  1527. if Valid then
  1528. begin
  1529. if not WasValid then
  1530. {New drive has arrived: insert new rootnode:}
  1531. begin
  1532. { Create root directory node }
  1533. NodeData := TNodeData.Create;
  1534. NodeData.DirName := DriveInfo.GetDriveRoot(Drive);
  1535. if Assigned(NextDriveNode) then
  1536. RootNode := Items.InsertObject(NextDriveNode, '', NodeData)
  1537. else
  1538. RootNode := Items.AddObject(nil, '', NodeData);
  1539. RootNode.Text := GetDisplayName(RootNode);
  1540. RootNode.HasChildren := True;
  1541. Scanned := False;
  1542. Verified := False;
  1543. end
  1544. else
  1545. if RootNode.ImageIndex <> DriveInfo.Get(Drive).ImageIndex then
  1546. begin {WasValid = True}
  1547. RootNode.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1548. RootNode.SelectedIndex := DriveInfo.Get(Drive).ImageIndex;
  1549. end;
  1550. if Assigned(RootNode) then
  1551. begin
  1552. NewText := GetDisplayName(RootNode);
  1553. if RootNode.Text <> NewText then
  1554. RootNode.Text := NewText;
  1555. end;
  1556. end
  1557. else
  1558. if WasValid then
  1559. {Drive has been removed => delete rootnode:}
  1560. begin
  1561. DriveRemoved(Drive);
  1562. Scanned := False;
  1563. Verified := False;
  1564. RootNode.Delete;
  1565. RootNode := nil;
  1566. end;
  1567. end;
  1568. end;
  1569. if Assigned(DriveStatus.RootNode) then
  1570. NextDriveNode := DriveStatus.RootNode;
  1571. end;
  1572. finally
  1573. Screen.Cursor := SaveCursor;
  1574. Drives.Free;
  1575. end;
  1576. end; {RefreshRootNodes}
  1577. procedure TDriveView.AddChildNode(ParentNode: TTreeNode; ParentPath: string; SRec: TSearchRec);
  1578. var
  1579. NewNode: TTreeNode;
  1580. NodeData: TNodeData;
  1581. begin
  1582. NodeData := TNodeData.Create;
  1583. NodeData.Attr := SRec.Attr;
  1584. NodeData.DirName := SRec.Name;
  1585. NodeData.FIsRecycleBin :=
  1586. (SRec.Attr and faSysFile <> 0) and
  1587. (ParentNode.Level = 0) and
  1588. (SameText(SRec.Name, 'RECYCLED') or
  1589. SameText(SRec.Name, 'RECYCLER') or
  1590. SameText(SRec.Name, '$RECYCLE.BIN'));
  1591. NodeData.Scanned := False;
  1592. NewNode := Self.Items.AddChildObject(ParentNode, '', NodeData);
  1593. NewNode.Text := GetDisplayName(NewNode);
  1594. NewNode.HasChildren := True;
  1595. if GetDriveTypeToNode(ParentNode) <> DRIVE_REMOTE then
  1596. FSubDirReaderThread.Add(NewNode, IncludeTrailingBackslash(ParentPath) + SRec.Name);
  1597. end; {AddChildNode}
  1598. function TDriveView.GetDriveStatus(Drive: string): TDriveStatus;
  1599. begin
  1600. if not FDriveStatus.TryGetValue(Drive, Result) then
  1601. begin
  1602. Result := CreateDriveStatus;
  1603. FDriveStatus.Add(Drive, Result);
  1604. RefreshRootNodes(dsAll or dvdsRereadAllways);
  1605. DoRefreshDrives(False);
  1606. end;
  1607. end; {GetDriveStatus}
  1608. function TDriveView.DoScanDir(FromNode: TTreeNode): Boolean;
  1609. begin
  1610. Result := not TNodeData(FromNode.Data).IsRecycleBin;
  1611. end; {DoScanDir}
  1612. function TDriveView.DirAttrMask: Integer;
  1613. begin
  1614. Result := faDirectory or faSysFile;
  1615. if ShowHiddenDirs then
  1616. Result := Result or faHidden;
  1617. end;
  1618. procedure TDriveView.ScanDrive(Drive: string);
  1619. begin {ScanDrive}
  1620. with Self.Items do
  1621. begin
  1622. ValidateDirectory(FindNodeToPath(DriveInfo.GetDriveRoot(Drive)));
  1623. GetDriveStatus(Drive).Scanned := True;
  1624. GetDriveStatus(Drive).Verified := False;
  1625. end;
  1626. end; {ScanDrive}
  1627. function TDriveView.DoFindNodeToPath(Path: string; ExistingOnly: Boolean): TTreeNode;
  1628. var
  1629. SelectionHierarchy: array of TTreeNode;
  1630. SelectionHierarchyHeight: Integer;
  1631. function SearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode; forward;
  1632. function ExtractFirstName(S: string): string;
  1633. var
  1634. I: Integer;
  1635. begin
  1636. I := Pos('\', S);
  1637. if I = 0 then
  1638. I := Length(S);
  1639. Result := System.Copy(S, 1, I);
  1640. end;
  1641. function DoSearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode;
  1642. var
  1643. Node: TTreeNode;
  1644. Dir: string;
  1645. begin
  1646. {Extract first directory from path:}
  1647. Dir := ExtractFirstName(Path);
  1648. System.Delete(Path, 1, Length(Dir));
  1649. if Dir[Length(Dir)] = '\' then
  1650. SetLength(Dir, Pred(Length(Dir)));
  1651. // Optimization. Avoid iterating possibly thousands of nodes,
  1652. // when the node we are looking for is the selected node or its ancestor.
  1653. // This is often the case, when navigating under node that has lot of sibligs.
  1654. // Typically, when navigating in user's profile folder, and there are many [thousands] other user profile folders.
  1655. if (SelectionHierarchyHeight > 0) and
  1656. // Change of selection might indicate that the tree was rebuilt meanwhile and
  1657. // the references in SelectionHierarchy might not be valid anymore
  1658. (Selected = SelectionHierarchy[SelectionHierarchyHeight - 1]) and
  1659. (Level < SelectionHierarchyHeight) and
  1660. (Uppercase(GetDirName(SelectionHierarchy[Level])) = Dir) then
  1661. begin
  1662. Result := SelectionHierarchy[Level];
  1663. end
  1664. else
  1665. begin
  1666. Node := ParentNode.GetFirstChild;
  1667. if (not Assigned(Node)) and (not ExistingOnly) then
  1668. begin
  1669. ValidateDirectoryEx(ParentNode, rsRecursiveExisting, True);
  1670. Node := ParentNode.GetFirstChild;
  1671. end;
  1672. Result := nil;
  1673. while (not Assigned(Result)) and Assigned(Node) do
  1674. begin
  1675. if UpperCase(GetDirName(Node)) = Dir then
  1676. begin
  1677. Result := Node;
  1678. end
  1679. else
  1680. begin
  1681. Node := ParentNode.GetNextChild(Node);
  1682. end;
  1683. end;
  1684. end;
  1685. if Assigned(Result) and (Length(Path) > 0) then
  1686. begin
  1687. Result := SearchSubDirs(Result, Path, Level + 1);
  1688. end;
  1689. end;
  1690. function SearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode;
  1691. var
  1692. ParentPath, SubPath: string;
  1693. SRec: TSearchRec;
  1694. begin
  1695. Result := nil;
  1696. if Length(Path) > 0 then
  1697. begin
  1698. if (not TNodeData(ParentNode.Data).Scanned) and (not ExistingOnly) then
  1699. begin
  1700. ReadSubDirs(ParentNode);
  1701. end;
  1702. // Factored out of DoSearchSubDirs is remnant of Bug 956 superceded by Bug 1320
  1703. Result := DoSearchSubDirs(ParentNode, Path, Level);
  1704. ParentPath := NodePath(ParentNode);
  1705. if (not Assigned(Result)) and
  1706. DirectoryExists(IncludeTrailingBackslash(ParentPath) + Path) and
  1707. (not ExistingOnly) then
  1708. begin
  1709. SubPath := IncludeTrailingBackslash(ParentPath) + ExcludeTrailingBackslash(ExtractFirstName(Path));
  1710. if FindFirstSubDir(SubPath, SRec) then
  1711. begin
  1712. AddChildNode(ParentNode, ParentPath, SRec);
  1713. SortChildren(ParentNode, False);
  1714. FindClose(SRec);
  1715. end;
  1716. Result := DoSearchSubDirs(ParentNode, Path, Level);
  1717. end;
  1718. end;
  1719. end; {SearchSubDirs}
  1720. var
  1721. Drive: string;
  1722. P, I: Integer;
  1723. RootNode, Node: TTreeNode;
  1724. begin {FindNodeToPath}
  1725. Result := nil;
  1726. if Length(Path) < 3 then
  1727. Exit;
  1728. // Particularly when used by TDirView to delegate browsing to
  1729. // hidden drive view, the handle may not be created
  1730. HandleNeeded;
  1731. Drive := DriveInfo.GetDriveKey(Path);
  1732. if (not Assigned(GetDriveStatus(Drive).RootNode)) and
  1733. // hidden or possibly recently un-hidden by other drive view (refresh is pending)
  1734. (DriveInfo.Get(Drive).Valid or DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy) then
  1735. begin
  1736. if DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy then
  1737. DriveInfo.OverrideDrivePolicy(Drive);
  1738. if DriveInfo.Get(Drive).Valid then
  1739. begin
  1740. CancelDriveRefresh; // cancel a possible pending refresh (see the previous comment)
  1741. RefreshRootNodes(dsAll or dvdsRereadAllways); // overkill and is likely already called by GetDriveStatus
  1742. DoRefreshDrives(False);
  1743. end;
  1744. end;
  1745. if Assigned(GetDriveStatus(Drive).RootNode) then
  1746. begin
  1747. if DriveInfo.IsRealDrive(Drive) then
  1748. begin
  1749. System.Delete(Path, 1, 3);
  1750. end
  1751. else
  1752. if IsUncPath(Path) then
  1753. begin
  1754. System.Delete(Path, 1, 2);
  1755. P := Pos('\', Path);
  1756. if P = 0 then
  1757. begin
  1758. Path := '';
  1759. end
  1760. else
  1761. begin
  1762. System.Delete(Path, 1, P);
  1763. P := Pos('\', Path);
  1764. if P = 0 then
  1765. begin
  1766. Path := '';
  1767. end
  1768. else
  1769. begin
  1770. System.Delete(Path, 1, P);
  1771. end;
  1772. end;
  1773. end
  1774. else
  1775. begin
  1776. raise EConvertError.Create(Format(ErrorInvalidDrive, [Path]))
  1777. end;
  1778. if Length(Path) > 0 then
  1779. begin
  1780. if (not GetDriveStatus(Drive).Scanned) and (not ExistingOnly) then
  1781. begin
  1782. ScanDrive(Drive);
  1783. end;
  1784. Node := Selected;
  1785. RootNode := GetDriveStatus(Drive).RootNode;
  1786. if not Assigned(Node) then
  1787. begin
  1788. SelectionHierarchyHeight := 0;
  1789. end
  1790. else
  1791. begin
  1792. SelectionHierarchyHeight := Node.Level + 1;
  1793. SetLength(SelectionHierarchy, SelectionHierarchyHeight);
  1794. for I := SelectionHierarchyHeight - 1 downto 0 do
  1795. begin
  1796. SelectionHierarchy[I] := Node;
  1797. Node := Node.Parent;
  1798. end;
  1799. Assert(Selected = SelectionHierarchy[SelectionHierarchyHeight - 1]);
  1800. Assert(RootNode = SelectionHierarchy[0]);
  1801. end;
  1802. Result := SearchSubDirs(RootNode, UpperCase(Path), 1);
  1803. end
  1804. else Result := GetDriveStatus(Drive).RootNode;
  1805. end;
  1806. end; {FindNodetoPath}
  1807. function TDriveView.FindNodeToPath(Path: string): TTreeNode;
  1808. begin
  1809. Result := DoFindNodeToPath(Path, False);
  1810. end;
  1811. function TDriveView.TryFindNodeToPath(Path: string): TTreeNode;
  1812. begin
  1813. Result := DoFindNodeToPath(Path, True);
  1814. end;
  1815. function TDriveView.GetSubDir(var SRec: TSearchRec): Boolean;
  1816. begin
  1817. Result := True;
  1818. while Result and
  1819. ((SRec.Name = '.' ) or
  1820. (SRec.Name = '..') or
  1821. ((SRec.Attr and faDirectory) = 0)) do
  1822. begin
  1823. if FindNext(SRec) <> 0 then
  1824. begin
  1825. Result := False;
  1826. end;
  1827. end;
  1828. end;
  1829. function TDriveView.FindFirstSubDir(Path: string; var SRec: TSearchRec): Boolean;
  1830. begin
  1831. Result := (FindFirstEx(ApiPath(Path), DirAttrMask, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS, FindExSearchLimitToDirectories) = 0);
  1832. if Result then
  1833. begin
  1834. Result := GetSubDir(SRec);
  1835. if not Result then FindClose(SRec);
  1836. end;
  1837. end;
  1838. function TDriveView.FindNextSubDir(var SRec: TSearchRec): Boolean;
  1839. begin
  1840. Result := (FindNext(SRec) = 0) and GetSubDir(SRec);
  1841. end;
  1842. procedure TDriveView.ReadSubDirs(Node: TTreeNode);
  1843. var
  1844. C: Integer;
  1845. SRec: TSearchRec;
  1846. Path: string;
  1847. Start: TDateTime;
  1848. R, Stop: Boolean;
  1849. begin
  1850. Path := NodePath(Node);
  1851. R := FindFirstSubDir(IncludeTrailingBackslash(Path) + '*.*', SRec);
  1852. Start := Now;
  1853. C := 0;
  1854. // At least from SetDirectory > DoFindNodeToPath and CanExpand, this is not called within BeginUpdate/EndUpdate block.
  1855. // But in any case, adding it here makes expanding (which calls CanExpand) noticeably slower, when there are lot of nodes,
  1856. // because EndUpdate triggers TVN_GETDISPINFO for all nodes in the tree.
  1857. while R do
  1858. begin
  1859. AddChildNode(Node, Path, SRec);
  1860. Inc(C);
  1861. // There are two other directory reading loops, where this is not called
  1862. if ((C mod 100) = 0) and Assigned(OnContinueLoading) then
  1863. begin
  1864. Stop := False;
  1865. OnContinueLoading(Self, Start, Path, C, Stop);
  1866. if Stop then R := False;
  1867. end;
  1868. if R then
  1869. begin
  1870. R := FindNextSubDir(SRec);
  1871. end;
  1872. end;
  1873. FindClose(Srec);
  1874. TNodeData(Node.Data).Scanned := True;
  1875. if C > 0 then SortChildren(Node, False)
  1876. else Node.HasChildren := False;
  1877. Application.ProcessMessages;
  1878. end; {ReadSubDirs}
  1879. procedure TDriveView.DeleteNode(Node: TTreeNode);
  1880. var
  1881. ValidNode: TTreeNode;
  1882. begin
  1883. if Assigned(Selected) and Assigned(Node.Parent) and
  1884. ((Selected = Node) or Selected.HasAsParent(Node)) then
  1885. begin
  1886. ValidNode := Node.Parent;
  1887. while (not DirectoryExists(NodePathName(ValidNode))) and Assigned(ValidNode.Parent) do
  1888. ValidNode := ValidNode.Parent;
  1889. Selected := ValidNode;
  1890. end;
  1891. if DropTarget = Node then
  1892. DropTarget := nil;
  1893. Node.Delete;
  1894. end;
  1895. function TDriveView.CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  1896. var
  1897. WorkNode: TTreeNode;
  1898. DelNode: TTreeNode;
  1899. SRec: TSearchRec;
  1900. SrecList: TStringList;
  1901. SubDirList: TStringList;
  1902. R: Boolean;
  1903. Index: Integer;
  1904. NewDirFound: Boolean;
  1905. ParentDir: string;
  1906. NodeData: TNodeData;
  1907. ScanDirInfo: PScanDirInfo;
  1908. begin {CallBackValidateDir}
  1909. Result := True;
  1910. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  1911. Exit;
  1912. NewDirFound := False;
  1913. ScanDirInfo := PScanDirInfo(Data);
  1914. {Check, if directory still exists: (but not with root directory) }
  1915. if Assigned(Node.Parent) and (ScanDirInfo^.StartNode = Node) then
  1916. if not DirectoryExists(NodePathName(Node)) then
  1917. begin
  1918. DeleteNode(Node);
  1919. Node := nil;
  1920. Exit;
  1921. end;
  1922. WorkNode := Node.GetFirstChild;
  1923. NodeData := TNodeData(Node.Data);
  1924. if NodeData.Scanned and Assigned(WorkNode) then
  1925. {if node was already scanned: check wether the existing subnodes are still alive
  1926. and add all new subdirectories as subnodes:}
  1927. begin
  1928. if DoScanDir(Node) then
  1929. begin
  1930. ParentDir := IncludeTrailingBackslash(NodePath(Node));
  1931. {Build list of existing subnodes:}
  1932. SubDirList := TStringList.Create;
  1933. SubDirList.CaseSensitive := True; // We want to reflect changes in subfolder name case
  1934. while Assigned(WorkNode) do
  1935. begin
  1936. SubDirList.Add(TNodeData(WorkNode.Data).DirName);
  1937. WorkNode := Node.GetNextChild(WorkNode);
  1938. end;
  1939. // Nodes are sorted using natural sorting, while TStringList.Find uses simple sorting
  1940. SubDirList.Sort;
  1941. SRecList := TStringList.Create;
  1942. SRecList.CaseSensitive := True;
  1943. R := FindFirstSubDir(ParentDir + '*.*', SRec);
  1944. while R do
  1945. begin
  1946. SrecList.Add(Srec.Name);
  1947. if not SubDirList.Find(Srec.Name, Index) then
  1948. {Subnode does not exists: add it:}
  1949. begin
  1950. AddChildNode(Node, ParentDir, SRec);
  1951. NewDirFound := True;
  1952. end;
  1953. R := FindNextSubDir(Srec);
  1954. end;
  1955. FindClose(Srec);
  1956. Sreclist.Sort;
  1957. {Remove not existing subnodes:}
  1958. WorkNode := Node.GetFirstChild;
  1959. while Assigned(WorkNode) do
  1960. begin
  1961. if not Assigned(WorkNode.Data) or
  1962. not SrecList.Find(TNodeData(WorkNode.Data).DirName, Index) then
  1963. begin
  1964. DelNode := WorkNode;
  1965. WorkNode := Node.GetNextChild(WorkNode);
  1966. DeleteNode(DelNode);
  1967. end
  1968. else
  1969. begin
  1970. if (SrecList[Index] <> TNodeData(WorkNode.Data).DirName) then
  1971. begin
  1972. {Case of directory letters has changed:}
  1973. TNodeData(WorkNode.Data).DirName := SrecList[Index];
  1974. WorkNode.Text := SrecList[Index];
  1975. end;
  1976. WorkNode := Node.GetNextChild(WorkNode);
  1977. end;
  1978. end;
  1979. SrecList.Free;
  1980. SubDirList.Free;
  1981. {Sort subnodes:}
  1982. if NewDirFound then
  1983. SortChildren(Node, False);
  1984. end;
  1985. end
  1986. else
  1987. {Node was not already scanned:}
  1988. if (ScanDirInfo^.SearchNewDirs or
  1989. NodeData.Scanned or
  1990. (Node = ScanDirInfo^.StartNode)) and
  1991. DoScanDir(Node) then
  1992. begin
  1993. ReadSubDirs(Node);
  1994. end;
  1995. end; {CallBackValidateDir}
  1996. procedure TDriveView.RebuildTree;
  1997. var
  1998. Drive: string;
  1999. begin
  2000. for Drive in FDriveStatus.Keys do
  2001. with GetDriveStatus(Drive) do
  2002. if Assigned(RootNode) and Scanned then
  2003. ValidateDirectory(RootNode);
  2004. end;
  2005. procedure TDriveView.ValidateCurrentDirectoryIfNotMonitoring;
  2006. begin
  2007. if Assigned(Selected) and
  2008. not Assigned(GetDriveStatus(GetDriveToNode(Selected)).DiscMonitor) then
  2009. begin
  2010. ValidateDirectory(Selected);
  2011. end;
  2012. end;
  2013. procedure TDriveView.ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  2014. NewDirs: Boolean);
  2015. var
  2016. Info: PScanDirInfo;
  2017. SelDir: string;
  2018. SaveCursor: TCursor;
  2019. RestartWatchThread: Boolean;
  2020. SaveCanChange: Boolean;
  2021. CurrentPath: string;
  2022. Drive: string;
  2023. begin
  2024. if Assigned(Node) and Assigned(Node.Data) and
  2025. (not FValidateFlag) and DoScanDir(Node) then
  2026. begin
  2027. SelDir := Directory;
  2028. SaveCursor := Screen.Cursor;
  2029. if Self.Focused and (Screen.Cursor <> crHourGlass) then
  2030. Screen.Cursor := crHourGlass;
  2031. CurrentPath := NodePath(Node);
  2032. Drive := DriveInfo.GetDriveKey(CurrentPath);
  2033. if Node.Level = 0 then
  2034. GetDriveStatus(Drive).ChangeTimer.Enabled := False;
  2035. RestartWatchThread := WatchThreadActive;
  2036. try
  2037. if WatchThreadActive then
  2038. StopWatchThread;
  2039. FValidateFlag := True;
  2040. New(Info);
  2041. Info^.StartNode := Node;
  2042. Info^.SearchNewDirs := NewDirs;
  2043. Info^.DriveType := DriveInfo.Get(Drive).DriveType;
  2044. SaveCanChange := FCanChange;
  2045. FCanChange := True;
  2046. FChangeFlag := False;
  2047. Items.BeginUpdate;
  2048. try
  2049. IterateSubTree(Node, CallBackValidateDir, Recurse, coScanStartNode, Info);
  2050. finally
  2051. Items.EndUpdate;
  2052. end;
  2053. FValidateFlag := False;
  2054. if (not Assigned(Selected)) and (Length(SelDir) > 0) then
  2055. Directory := ExtractFileDrive(SelDir);
  2056. if (SelDir <> Directory) and (not FChangeFlag) then
  2057. Change(Selected);
  2058. FCanChange := SaveCanChange;
  2059. Dispose(Info);
  2060. finally
  2061. if RestartWatchThread and FWatchDirectory and not WatchThreadActive then
  2062. StartWatchThread;
  2063. if Screen.Cursor <> SaveCursor then
  2064. Screen.Cursor := SaveCursor;
  2065. end;
  2066. end;
  2067. end; {ValidateDirectoryEx}
  2068. function TDriveView.GetDriveTypeToNode(Node: TTreeNode): Integer;
  2069. begin
  2070. Assert(Assigned(Node));
  2071. Result := DriveInfo.Get(GetDriveToNode(Node)).DriveType;
  2072. end; {GetDriveTypeToNode}
  2073. procedure TDriveView.CreateWatchThread(Drive: string);
  2074. begin
  2075. if csDesigning in ComponentState then
  2076. Exit;
  2077. if (not Assigned(GetDriveStatus(Drive).DiscMonitor)) and
  2078. FWatchDirectory and
  2079. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) then
  2080. begin
  2081. with GetDriveStatus(Drive) do
  2082. begin
  2083. DiscMonitor := TDiscMonitor.Create(Self);
  2084. DiscMonitor.ChangeDelay := msThreadChangeDelay;
  2085. DiscMonitor.SubTree := True;
  2086. DiscMonitor.Filters := [moDirName];
  2087. DiscMonitor.OnChange := ChangeDetected;
  2088. DiscMonitor.OnInvalid := ChangeInvalid;
  2089. DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
  2090. DiscMonitor.Open;
  2091. end;
  2092. UpdateDriveNotifications(Drive);
  2093. end;
  2094. end; {CreateWatchThread}
  2095. procedure TDriveView.SetWatchDirectory(Value: Boolean);
  2096. begin
  2097. if FWatchDirectory <> Value then
  2098. begin
  2099. FWatchDirectory := Value;
  2100. if (not (csDesigning in ComponentState)) and Value then
  2101. StartAllWatchThreads
  2102. else
  2103. StopAllWatchThreads;
  2104. end;
  2105. end; {SetAutoScan}
  2106. procedure TDriveView.SetDirView(Value: TDirView);
  2107. begin
  2108. if Assigned(FDirView) then
  2109. FDirView.DriveView := nil;
  2110. FDirView := Value;
  2111. if Assigned(FDirView) then
  2112. FDirView.DriveView := Self;
  2113. end; {SetDirView}
  2114. function TDriveView.NodeWatched(Node: TTreeNode): Boolean;
  2115. var
  2116. Drive: string;
  2117. begin
  2118. Drive := GetDriveToNode(Node);
  2119. Result := WatchThreadActive(Drive);
  2120. end; {NodeWatched}
  2121. procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
  2122. const ErrorStr: string);
  2123. var
  2124. Drive: string;
  2125. begin
  2126. Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
  2127. with GetDriveStatus(Drive) do
  2128. begin
  2129. DiscMonitor.Close;
  2130. end;
  2131. UpdateDriveNotifications(Drive);
  2132. end; {DirWatchChangeInvalid}
  2133. procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
  2134. var SubdirsChanged: Boolean);
  2135. var
  2136. DirChanged: string;
  2137. begin
  2138. if Sender is TDiscMonitor then
  2139. begin
  2140. DirChanged := (Sender as TDiscMonitor).Directories[0];
  2141. if Length(DirChanged) > 0 then
  2142. begin
  2143. with GetDriveStatus(DriveInfo.GetDriveKey(DirChanged)) do
  2144. begin
  2145. ChangeTimer.Interval := 0;
  2146. ChangeTimer.Interval := FChangeInterval;
  2147. ChangeTimer.Enabled := True;
  2148. end;
  2149. end;
  2150. end;
  2151. end; {DirWatchChangeDetected}
  2152. procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
  2153. var
  2154. DriveStatusPair: TDriveStatusPair;
  2155. begin
  2156. if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
  2157. begin
  2158. for DriveStatusPair in FDriveStatus do
  2159. begin
  2160. if DriveStatusPair.Value.ChangeTimer = Sender then
  2161. begin
  2162. // Messages are processed during ValidateDirectory, so we may detect another change while
  2163. // updating the directory. Prevent the recursion.
  2164. // But retry the update afterwards (by reenabling the timer in ChangeDetected)
  2165. SuspendChangeTimer;
  2166. try
  2167. with DriveStatusPair.Value.ChangeTimer do
  2168. begin
  2169. Interval := 0;
  2170. Enabled := False;
  2171. end;
  2172. if Assigned(DriveStatusPair.Value.RootNode) then
  2173. begin
  2174. {Check also collapsed (invisible) subdirectories:}
  2175. ValidateDirectory(DriveStatusPair.Value.RootNode);
  2176. end;
  2177. finally
  2178. ResumeChangeTimer;
  2179. end;
  2180. end;
  2181. end;
  2182. end;
  2183. end; {ChangeTimerOnTimer}
  2184. procedure TDriveView.UpdateDriveNotifications(Drive: string);
  2185. var
  2186. NeedNotifications: Boolean;
  2187. Path: string;
  2188. DevBroadcastHandle: DEV_BROADCAST_HANDLE;
  2189. Size: Integer;
  2190. begin
  2191. if DriveInfo.IsFixedDrive(Drive) then
  2192. begin
  2193. with GetDriveStatus(Drive) do
  2194. begin
  2195. NeedNotifications :=
  2196. WatchThreadActive(Drive) and
  2197. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) and
  2198. DriveInfo.Get(Drive).DriveReady;
  2199. if NeedNotifications <> (DriveHandle <> INVALID_HANDLE_VALUE) then
  2200. begin
  2201. if NeedNotifications then
  2202. begin
  2203. Path := DriveInfo.GetDriveRoot(Drive);
  2204. DriveHandle :=
  2205. CreateFile(PChar(Path), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
  2206. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0);
  2207. if DriveHandle <> INVALID_HANDLE_VALUE then
  2208. begin
  2209. Size := SizeOf(DevBroadcastHandle);
  2210. ZeroMemory(@DevBroadcastHandle, Size);
  2211. DevBroadcastHandle.dbch_size := Size;
  2212. DevBroadcastHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
  2213. DevBroadcastHandle.dbch_handle := DriveHandle;
  2214. NotificationHandle :=
  2215. RegisterDeviceNotification(FInternalWindowHandle, @DevBroadcastHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  2216. if NotificationHandle = nil then
  2217. begin
  2218. CloseHandle(DriveHandle);
  2219. DriveHandle := INVALID_HANDLE_VALUE;
  2220. end;
  2221. end;
  2222. end
  2223. else
  2224. begin
  2225. UnregisterDeviceNotification(NotificationHandle);
  2226. NotificationHandle := nil;
  2227. CloseHandle(DriveHandle);
  2228. DriveHandle := INVALID_HANDLE_VALUE;
  2229. end;
  2230. end;
  2231. end;
  2232. end;
  2233. end;
  2234. procedure TDriveView.StartWatchThread;
  2235. var
  2236. Drive: string;
  2237. begin
  2238. if (csDesigning in ComponentState) or
  2239. not Assigned(Selected) or
  2240. not fWatchDirectory then Exit;
  2241. Drive := GetDriveToNode(Selected);
  2242. with GetDriveStatus(Drive) do
  2243. begin
  2244. if not Assigned(DiscMonitor) then
  2245. CreateWatchThread(Drive);
  2246. if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
  2247. DiscMonitor.Enabled := True;
  2248. end;
  2249. UpdateDriveNotifications(Drive);
  2250. end; {StartWatchThread}
  2251. procedure TDriveView.StopWatchThread;
  2252. var
  2253. Drive: string;
  2254. begin
  2255. if Assigned(Selected) then
  2256. begin
  2257. Drive := GetDriveToNode(Selected);
  2258. with GetDriveStatus(Drive) do
  2259. if Assigned(DiscMonitor) then
  2260. DiscMonitor.Enabled := False;
  2261. UpdateDriveNotifications(Drive);
  2262. end;
  2263. end; {StopWatchThread}
  2264. procedure TDriveView.SuspendChangeTimer;
  2265. begin
  2266. Inc(FChangeTimerSuspended);
  2267. end;
  2268. procedure TDriveView.ResumeChangeTimer;
  2269. begin
  2270. Assert(FChangeTimerSuspended > 0);
  2271. Dec(FChangeTimerSuspended);
  2272. end;
  2273. procedure TDriveView.TerminateWatchThread(Drive: string);
  2274. begin
  2275. with GetDriveStatus(Drive) do
  2276. if Assigned(DiscMonitor) then
  2277. begin
  2278. DiscMonitor.Free;
  2279. DiscMonitor := nil;
  2280. end;
  2281. UpdateDriveNotifications(Drive);
  2282. end; {StopWatchThread}
  2283. procedure TDriveView.StartAllWatchThreads;
  2284. var
  2285. DriveStatusPair: TDriveStatusPair;
  2286. Drive: string;
  2287. begin
  2288. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2289. Exit;
  2290. for DriveStatusPair in FDriveStatus do
  2291. with DriveStatusPair.Value do
  2292. if Scanned then
  2293. begin
  2294. if not Assigned(DiscMonitor) then
  2295. CreateWatchThread(DriveStatusPair.Key);
  2296. if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
  2297. begin
  2298. DiscMonitor.Open;
  2299. UpdateDriveNotifications(DriveStatusPair.Key);
  2300. end;
  2301. end;
  2302. if Assigned(Selected) then
  2303. begin
  2304. Drive := GetDriveToNode(Selected);
  2305. if not DriveInfo.IsFixedDrive(Drive) then
  2306. begin
  2307. StartWatchThread;
  2308. end;
  2309. end;
  2310. end; {StartAllWatchThreads}
  2311. procedure TDriveView.StopAllWatchThreads;
  2312. var
  2313. DriveStatusPair: TDriveStatusPair;
  2314. begin
  2315. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2316. Exit;
  2317. for DriveStatusPair in FDriveStatus do
  2318. with DriveStatusPair.Value do
  2319. begin
  2320. if Assigned(DiscMonitor) then
  2321. begin
  2322. DiscMonitor.Close;
  2323. UpdateDriveNotifications(DriveStatusPair.Key);
  2324. end;
  2325. end;
  2326. end; {StopAllWatchThreads}
  2327. function TDriveView.WatchThreadActive(Drive: string): Boolean;
  2328. begin
  2329. Result := FWatchDirectory and
  2330. Assigned(GetDriveStatus(Drive).DiscMonitor) and
  2331. GetDriveStatus(Drive).DiscMonitor.Active and
  2332. GetDriveStatus(Drive).DiscMonitor.Enabled;
  2333. end; {WatchThreadActive}
  2334. function TDriveView.WatchThreadActive: Boolean;
  2335. var
  2336. Drive: string;
  2337. begin
  2338. if not Assigned(Selected) then
  2339. begin
  2340. Result := False;
  2341. Exit;
  2342. end;
  2343. Drive := GetDriveToNode(Selected);
  2344. Result := WatchThreadActive(Drive);
  2345. end; {WatchThreadActive}
  2346. function TDriveView.FindPathNode(Path: string): TTreeNode;
  2347. var
  2348. PossiblyHiddenPath: string;
  2349. Attrs: Integer;
  2350. begin
  2351. if Assigned(FOnNeedHiddenDirectories) and
  2352. (not ShowHiddenDirs) and
  2353. DirectoryExistsFix(Path) then // do not even bother if the path does not exist
  2354. begin
  2355. PossiblyHiddenPath := ExcludeTrailingPathDelimiter(Path);
  2356. while (PossiblyHiddenPath <> '') and
  2357. (not IsRootPath(PossiblyHiddenPath)) do // Drives have hidden attribute
  2358. begin
  2359. Attrs := FileGetAttr(PossiblyHiddenPath, False);
  2360. if (Attrs and faHidden) = faHidden then
  2361. begin
  2362. if Assigned(FOnNeedHiddenDirectories) then
  2363. begin
  2364. FOnNeedHiddenDirectories(Self);
  2365. end;
  2366. Break;
  2367. end
  2368. else
  2369. begin
  2370. PossiblyHiddenPath := ExtractFileDir(PossiblyHiddenPath);
  2371. end;
  2372. end;
  2373. end;
  2374. {Find existing path or parent path of not existing path:}
  2375. repeat
  2376. Result := FindNodeToPath(Path);
  2377. if not Assigned(Result) then
  2378. Path := ExtractFilePath(ExcludeTrailingBackslash(Path));
  2379. until Assigned(Result) or (Length(Path) < 3);
  2380. end;
  2381. procedure TDriveView.SetDirectory(Value: string);
  2382. begin
  2383. Value := IncludeTrailingBackslash(Value);
  2384. FDirectory := Value;
  2385. inherited;
  2386. if Assigned(Selected) and (Selected.Level = 0) then
  2387. begin
  2388. if not GetDriveStatus(GetDriveToNode(Selected)).Scanned then
  2389. ScanDrive(GetDriveToNode(Selected));
  2390. end;
  2391. end; {SetDirectory}
  2392. function TDriveView.GetDirName(Node: TTreeNode): string;
  2393. begin
  2394. if Assigned(Node) and Assigned(Node.Data) then
  2395. Result := TNodeData(Node.Data).DirName
  2396. else
  2397. Result := '';
  2398. end; {GetDirName}
  2399. {GetDrive: returns the drive of the Node.}
  2400. function TDriveView.GetDriveToNode(Node: TTreeNode): string;
  2401. var
  2402. Path: string;
  2403. begin
  2404. if (not Assigned (Node)) or (not Assigned(Node.Data)) then
  2405. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDrive']));
  2406. Path := NodePath(Node);
  2407. Result := DriveInfo.GetDriveKey(Path);
  2408. end; {GetDrive}
  2409. {RootNode: returns the rootnode to the Node:}
  2410. function TDriveView.RootNode(Node: TTreeNode): TTreeNode;
  2411. begin
  2412. Result := Node;
  2413. if not Assigned(Node) then
  2414. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['RootNode']));
  2415. while Assigned(Result.Parent) do
  2416. Result := Result.Parent;
  2417. end; {RootNode}
  2418. function TDriveView.GetDisplayName(Node: TTreeNode): string;
  2419. begin
  2420. Result := '';
  2421. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  2422. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDisplayName']));
  2423. if Node.Level = 0 then Result := GetDriveText(GetDriveToNode(Node))
  2424. else
  2425. begin
  2426. Result := GetDirName(Node);
  2427. end;
  2428. end; {GetDisplayName}
  2429. procedure TDriveView.SetShowVolLabel(ShowIt: Boolean);
  2430. begin
  2431. if ShowIt = FShowVolLabel then
  2432. Exit;
  2433. FShowVolLabel := ShowIt;
  2434. RefreshRootNodes(dvdsFloppy);
  2435. end; {SetShowVolLabel}
  2436. procedure TDriveView.DisplayContextMenu(Node: TTreeNode; Point: TPoint);
  2437. var
  2438. Verb: string;
  2439. DirWatched: Boolean;
  2440. begin
  2441. DirWatched := NodeWatched(Node) and WatchThreadActive;
  2442. Assert(Node <> nil);
  2443. if Node <> Selected then
  2444. DropTarget := Node;
  2445. Verb := EmptyStr;
  2446. if Assigned(FOnDisplayContextMenu) then
  2447. FOnDisplayContextMenu(Self);
  2448. ShellDisplayContextMenu(FParentForm.Handle, Point, NodePathName(Node),
  2449. CanEdit(Node), Verb, False);
  2450. if Verb = shcRename then Node.EditText
  2451. else
  2452. if Verb = shcCut then
  2453. begin
  2454. LastClipBoardOperation := cboCut;
  2455. LastPathCut := NodePathName(Node);
  2456. end
  2457. else
  2458. if Verb = shcCopy then LastClipBoardOperation := cboCopy
  2459. else
  2460. if Verb = shcPaste then
  2461. PasteFromClipBoard(NodePathName(Node));
  2462. DropTarget := nil;
  2463. if not DirWatched then
  2464. ValidateDirectory(Node);
  2465. end; {DisplayContextMenu (2)}
  2466. procedure TDriveView.DisplayPropertiesMenu(Node: TTreeNode);
  2467. begin
  2468. Assert(Assigned(Node));
  2469. ShellExecuteContextCommand(FParentForm.Handle, shcProperties, NodePathName(Node));
  2470. end; {ContextMenu}
  2471. procedure TDriveView.SetSelected(Node: TTreeNode);
  2472. begin
  2473. if Node <> Selected then
  2474. begin
  2475. FChangeFlag := False;
  2476. FCanChange := True;
  2477. inherited Selected := Node;
  2478. if not FChangeFlag then
  2479. Change(Selected);
  2480. end;
  2481. end; {SetSelected}
  2482. {Called by TFileDeleteThread, when a file deletion was detected by the D&D receiving application:}
  2483. procedure TDriveView.SignalDirDelete(Sender: TObject; Files: TStringList);
  2484. begin
  2485. if Files.Count > 0 then
  2486. ValidateDirectory(FindNodeToPath(Files[0]));
  2487. end; {SignalDirDelete}
  2488. function TDriveView.DDSourceEffects: TDropEffectSet;
  2489. begin
  2490. if FDragNode.Level = 0 then
  2491. Result := [deLink]
  2492. else
  2493. Result := [deLink, deCopy, deMove];
  2494. end;
  2495. procedure TDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);
  2496. begin
  2497. if DropTarget = nil then Effect := DROPEFFECT_NONE
  2498. else
  2499. if (KeyState and (MK_CONTROL or MK_SHIFT) = 0) and (PreferredEffect = 0) then
  2500. begin
  2501. if FDragDrive <> '' then
  2502. begin
  2503. if FExeDrag and DriveInfo.IsFixedDrive(GetDriveToNode(DropTarget)) and DriveInfo.IsFixedDrive(FDragDrive) then
  2504. begin
  2505. Effect := DROPEFFECT_LINK;
  2506. end
  2507. else
  2508. if (Effect = DROPEFFECT_COPY) and
  2509. (SameText(FDragDrive, GetDriveToNode(DropTarget)) and
  2510. (FDragDropFilesEx.AvailableDropEffects and DROPEFFECT_MOVE <> 0)) then
  2511. begin
  2512. Effect := DROPEFFECT_MOVE;
  2513. end;
  2514. end;
  2515. end;
  2516. inherited;
  2517. end;
  2518. function TDriveView.DragCompleteFileList: Boolean;
  2519. begin
  2520. Result := (GetDriveTypeToNode(FDragNode) <> DRIVE_REMOVABLE);
  2521. end;
  2522. function TDriveView.DDExecute: TDragResult;
  2523. var
  2524. WatchThreadOK: Boolean;
  2525. DragParentPath: string;
  2526. DragPath: string;
  2527. begin
  2528. WatchThreadOK := WatchThreadActive;
  2529. Result := FDragDropFilesEx.Execute(nil);
  2530. if (Result = drMove) and (not WatchThreadOK) then
  2531. begin
  2532. DragPath := NodePathName(FDragNode);
  2533. if Assigned(FDragNode.Parent) then
  2534. DragParentPath := NodePathName(FDragNode.Parent)
  2535. else
  2536. DragParentPath := DragPath;
  2537. if (FDragNode.Level > 0) or (DragParentPath <> NodePathName(Selected.Parent)) then
  2538. begin
  2539. FDragNode := FindNodeToPath(DragPath);
  2540. if Assigned(FDragNode) then
  2541. begin
  2542. FDragFileList.Clear;
  2543. FDragFileList.Add(DragPath);
  2544. TFileDeleteThread.Create(FDragFileList, MaxWaitTimeOut, SignalDirDelete);
  2545. end;
  2546. end;
  2547. end;
  2548. end;
  2549. procedure TDriveView.PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer);
  2550. var
  2551. Index: Integer;
  2552. SourcePath: string;
  2553. SourceParentPath: string;
  2554. SourceIsDirectory: Boolean;
  2555. SaveCursor: TCursor;
  2556. SourceNode, TargetNode: TTreeNode;
  2557. TargetPath: string;
  2558. IsRecycleBin: Boolean;
  2559. begin
  2560. TargetPath := NodePathName(Node);
  2561. IsRecycleBin := NodeIsRecycleBin(Node);
  2562. if FDragDropFilesEx.FileList.Count = 0 then
  2563. Exit;
  2564. SaveCursor := Screen.Cursor;
  2565. Screen.Cursor := crHourGlass;
  2566. SourcePath := EmptyStr;
  2567. try
  2568. if (Effect = DROPEFFECT_COPY) or (Effect = DROPEFFECT_MOVE) then
  2569. begin
  2570. StopAllWatchThreads;
  2571. if Assigned(FDirView) then
  2572. FDirView.StopWatchThread;
  2573. if Assigned(DropSourceControl) and
  2574. (DropSourceControl is TDirView) and
  2575. (DropSourceControl <> FDirView) then
  2576. begin
  2577. TDirView(DropSourceControl).StopWatchThread;
  2578. end;
  2579. if DropFiles(
  2580. DragDropFilesEx, Effect, FFileOperator, TargetPath, false, IsRecycleBin, ConfirmDelete, ConfirmOverwrite, False,
  2581. Self, OnDDFileOperation, SourcePath, SourceIsDirectory) then
  2582. begin
  2583. if Assigned(FOnDDFileOperationExecuted) then
  2584. FOnDDFileOperationExecuted(Self, Effect, SourcePath, TargetPath);
  2585. end;
  2586. ClearDragFileList(FDragDropFilesEx.FileList);
  2587. // TDirView.PerformDragDropFileOperation validates the SourcePath and that actually seems correct
  2588. SourceParentPath := ExtractFilePath(ExcludeTrailingBackslash(SourcePath));
  2589. end
  2590. else
  2591. if Effect = DROPEFFECT_LINK then
  2592. { Create Link requested: }
  2593. begin
  2594. for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do
  2595. begin
  2596. if not DropLink(PFDDListItem(FDragDropFilesEx.FileList[Index]), TargetPath) then
  2597. begin
  2598. DDError(DDCreateShortCutError);
  2599. end;
  2600. end;
  2601. end;
  2602. if Effect = DROPEFFECT_MOVE then
  2603. Items.BeginUpdate;
  2604. {Update source directory, if move-operation was performed:}
  2605. if ((Effect = DROPEFFECT_MOVE) or IsRecycleBin) then
  2606. begin
  2607. // See comment in corresponding operation in TDirView.PerformDragDropFileOperation
  2608. SourceNode := TryFindNodeToPath(SourceParentPath);
  2609. if Assigned(SourceNode) then
  2610. ValidateDirectory(SourceNode);
  2611. end;
  2612. {Update subdirectories of target directory:}
  2613. TargetNode := FindNodeToPath(TargetPath);
  2614. if Assigned(TargetNode) then
  2615. ValidateDirectory(TargetNode)
  2616. else
  2617. ValidateDirectory(GetDriveStatus(DriveInfo.GetDriveKey(TargetPath)).RootNode);
  2618. if Effect = DROPEFFECT_MOVE then
  2619. Items.EndUpdate;
  2620. {Update linked component TDirView:}
  2621. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2622. begin
  2623. case Effect of
  2624. DROPEFFECT_COPY,
  2625. DROPEFFECT_LINK:
  2626. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) then
  2627. FDirView.Reload2;
  2628. DROPEFFECT_MOVE:
  2629. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) or
  2630. (IncludeTrailingBackslash(SourceParentPath) = IncludeTrailingBackslash(DirView.Path)) then
  2631. begin
  2632. if FDirView <> DropSourceControl then FDirView.Reload2;
  2633. end;
  2634. end; {Case}
  2635. end;
  2636. {Update the DropSource control, if files are moved and it is a TDirView:}
  2637. if (Effect = DROPEFFECT_MOVE) and (DropSourceControl is TDirView) then
  2638. begin
  2639. TDirView(DropSourceControl).ValidateSelectedFiles;
  2640. end;
  2641. finally
  2642. FFileOperator.OperandFrom.Clear;
  2643. FFileOperator.OperandTo.Clear;
  2644. StartAllWatchThreads;
  2645. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2646. FDirView.StartWatchThread;
  2647. if Assigned(DropSourceControl) and (DropSourceControl is TDirView) and
  2648. (not TDirView(DropSourceControl).WatchThreadActive) then
  2649. TDirView(DropSourceControl).StartWatchThread;
  2650. Screen.Cursor := SaveCursor;
  2651. end;
  2652. end; {PerformDragDropFileOperation}
  2653. function TDriveView.GetCanUndoCopyMove: Boolean;
  2654. begin
  2655. Result := Assigned(FFileOperator) and FFileOperator.CanUndo;
  2656. end; {CanUndoCopyMove}
  2657. function TDriveView.UndoCopyMove: Boolean;
  2658. var
  2659. LastTarget: string;
  2660. LastSource: string;
  2661. begin
  2662. Result := False;
  2663. if FFileOperator.CanUndo then
  2664. begin
  2665. Lasttarget := FFileOperator.LastOperandTo[0];
  2666. LastSource := FFileOperator.LastOperandFrom[0];
  2667. StopAllWatchThreads;
  2668. Result := FFileOperator.UndoExecute;
  2669. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastTarget)));
  2670. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastSource)));
  2671. StartAllWatchThreads;
  2672. if Assigned(FDirView) then
  2673. with FDirView do
  2674. if not WatchThreadActive then
  2675. begin
  2676. if (IncludeTrailingBackslash(ExtractFilePath(LastTarget)) = IncludeTrailingBackslash(Path)) or
  2677. (IncludeTrailingBackslash(ExtractFilePath(LastSource)) = IncludeTrailingBackslash(Path)) then
  2678. Reload2;
  2679. end;
  2680. end;
  2681. end; {UndoCopyMove}
  2682. {Clipboard operations:}
  2683. procedure TDriveView.SetLastPathCut(Path: string);
  2684. var
  2685. Node: TTreeNode;
  2686. begin
  2687. if FLastPathCut <> Path then
  2688. begin
  2689. Node := FindNodeToPath(FLastPathCut);
  2690. if Assigned(Node) then
  2691. begin
  2692. FLastPathCut := Path;
  2693. Node.Cut := False;
  2694. end;
  2695. Node := FindNodeToPath(Path);
  2696. if Assigned(Node) then
  2697. begin
  2698. FLastPathCut := Path;
  2699. Node.Cut := True;
  2700. end;
  2701. end;
  2702. end; {SetLastNodeCut}
  2703. procedure TDriveView.EmptyClipboard;
  2704. begin
  2705. if Windows.OpenClipBoard(0) then
  2706. begin
  2707. Windows.EmptyClipBoard;
  2708. Windows.CloseClipBoard;
  2709. LastPathCut := '';
  2710. LastClipBoardOperation := cboNone;
  2711. if Assigned(FDirView) then
  2712. FDirView.EmptyClipboard;
  2713. end;
  2714. end; {EmptyClipBoard}
  2715. function TDriveView.CopyToClipBoard(Node: TTreeNode): Boolean;
  2716. begin
  2717. Result := Assigned(Selected);
  2718. if Result then
  2719. begin
  2720. EmptyClipBoard;
  2721. ClearDragFileList(FDragDropFilesEx.FileList);
  2722. AddToDragFileList(FDragDropFilesEx.FileList, Selected);
  2723. Result := FDragDropFilesEx.CopyToClipBoard;
  2724. LastClipBoardOperation := cboCopy;
  2725. end;
  2726. end; {CopyToClipBoard}
  2727. function TDriveView.CutToClipBoard(Node: TTreeNode): Boolean;
  2728. begin
  2729. Result := Assigned(Node) and (Node.Level > 0) and CopyToClipBoard(Node);
  2730. if Result then
  2731. begin
  2732. LastPathCut := NodePathName(Node);
  2733. LastClipBoardOperation := cboCut;
  2734. end;
  2735. end; {CutToClipBoard}
  2736. function TDriveView.CanPasteFromClipBoard: Boolean;
  2737. begin
  2738. Result := False;
  2739. if Assigned(Selected) and Windows.OpenClipboard(0) then
  2740. begin
  2741. Result := IsClipboardFormatAvailable(CF_HDROP);
  2742. Windows.CloseClipBoard;
  2743. end;
  2744. end; {CanPasteFromClipBoard}
  2745. function TDriveView.PasteFromClipBoard(TargetPath: String = ''): Boolean;
  2746. begin
  2747. ClearDragFileList(FDragDropFilesEx.FileList);
  2748. Result := False;
  2749. if CanPasteFromClipBoard and {MP}FDragDropFilesEx.GetFromClipBoard{/MP}
  2750. then
  2751. begin
  2752. if TargetPath = '' then
  2753. TargetPath := NodePathName(Selected);
  2754. case LastClipBoardOperation of
  2755. cboCopy,
  2756. cboNone:
  2757. begin
  2758. PerformDragDropFileOperation(Selected, DROPEFFECT_COPY);
  2759. if Assigned(FOnDDExecuted) then
  2760. FOnDDExecuted(Self, DROPEFFECT_COPY);
  2761. end;
  2762. cboCut:
  2763. begin
  2764. PerformDragDropFileOperation(Selected, DROPEFFECT_MOVE);
  2765. if Assigned(FOnDDExecuted) then
  2766. FOnDDExecuted(Self, DROPEFFECT_MOVE);
  2767. EmptyClipBoard;
  2768. end;
  2769. end;
  2770. Result := True;
  2771. end;
  2772. end; {PasteFromClipBoard}
  2773. procedure TDriveView.CMRecreateWnd(var Msg: TMessage);
  2774. var
  2775. ScheduledCount: Integer;
  2776. begin
  2777. ScheduledCount := FSubDirReaderThread.Detach;
  2778. try
  2779. inherited;
  2780. finally
  2781. FSubDirReaderThread.Reattach(ScheduledCount);
  2782. end;
  2783. end;
  2784. end.