DriveView.pas 88 KB

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