DriveView.pas 88 KB

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