1
0

DriveView.pas 78 KB

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