DriveView.pas 80 KB

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