DriveView.pas 79 KB

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