DriveView.pas 78 KB

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