DriveView.pas 92 KB

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