DriveView.pas 78 KB

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