DriveView.pas 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301
  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, BaseUtils, CustomDirView,
  35. CustomDriveView, System.Generics.Collections, CompThread;
  36. const
  37. msThreadChangeDelay = 50;
  38. ErrorNodeNA = '%s: Node not assigned';
  39. {Flags used by TDriveView.RefreshRootNodes:}
  40. dvdsFloppy = 8; {Include floppy drives}
  41. dvdsRereadAllways = 16; {Refresh drivestatus in any case}
  42. WM_USER_SHCHANGENOTIFY = WM_USER + $2000 + 13;
  43. WM_USER_SUBDIRREADER = WM_USER_SHCHANGENOTIFY + 1;
  44. type
  45. EInvalidDirName = class(Exception);
  46. ENodeNotAssigned = class(Exception);
  47. TDriveStatus = class
  48. Scanned: Boolean; {Drive allready scanned?}
  49. Verified: Boolean; {Drive completly scanned?}
  50. RootNode: TTreeNode; {Rootnode to drive}
  51. RootNodeIndex: Integer;
  52. DiscMonitor: TDiscMonitor; {Monitor thread}
  53. ChangeTimer: TTimer; {Change timer for the monitor thread}
  54. DefaultDir: string; {Current directory}
  55. DriveHandle: THandle;
  56. NotificationHandle: HDEVNOTIFY;
  57. end;
  58. TDriveStatusPair = TPair<string, TDriveStatus>;
  59. TScanDirInfo = record
  60. SearchNewDirs: Boolean;
  61. StartNode: TTreeNode;
  62. DriveType: Integer;
  63. end;
  64. PScanDirInfo = ^TScanDirInfo;
  65. TDriveView = class;
  66. TSubDirReaderSchedule = class
  67. Node: TTreeNode;
  68. Path: string;
  69. Deleted: Boolean;
  70. Processed: Boolean;
  71. end;
  72. TNodeData = class
  73. private
  74. FDirName: string;
  75. FAttr: Integer;
  76. FScanned: Boolean;
  77. FData: Pointer;
  78. FIsRecycleBin: Boolean;
  79. FIconEmpty: Boolean;
  80. FSchedule: TSubDirReaderSchedule;
  81. public
  82. DelayedSrec: TSearchRec;
  83. DelayedExclude: TStringList;
  84. constructor Create;
  85. destructor Destroy; override;
  86. property DirName: string read FDirName write FDirName;
  87. property Attr: Integer read FAttr write FAttr;
  88. property Scanned: Boolean read FScanned write FScanned;
  89. property Data: Pointer read FData write FData;
  90. property IsRecycleBin: Boolean read FIsRecycleBin;
  91. property IconEmpty: Boolean read FIconEmpty write FIconEmpty;
  92. property Schedule: TSubDirReaderSchedule read FSchedule write FSchedule;
  93. end;
  94. TDriveTreeNode = class(TTreeNode)
  95. procedure Assign(Source: TPersistent); override;
  96. end;
  97. TSubDirReaderThread = class(TCompThread)
  98. public
  99. destructor Destroy; override;
  100. procedure Terminate; override;
  101. protected
  102. constructor Create(DriveView: TDriveView);
  103. procedure Add(Node: TTreeNode; Path: string);
  104. procedure Delete(Node: TTreeNode);
  105. function Detach: Integer;
  106. procedure Reattach(Count: Integer);
  107. procedure Execute; override;
  108. private
  109. FDriveView: TDriveView;
  110. FEvent: THandle;
  111. FQueue: TStack<TSubDirReaderSchedule>;
  112. FResults: TQueue<TSubDirReaderSchedule>;
  113. FSection: TRTLCriticalSection;
  114. FTimer: TTimer;
  115. FWindowHandle: HWND;
  116. procedure TriggerEvent;
  117. procedure ScheduleProcess;
  118. procedure Process;
  119. function ProcessResult: Boolean;
  120. procedure Timer(Sender: TObject);
  121. procedure WndProc(var Msg: TMessage);
  122. end;
  123. TDriveViewRefreshDrives = procedure(Sender: TObject; Global: Boolean) of object;
  124. TDriveView = class(TCustomDriveView)
  125. private
  126. FDriveStatus: TObjectDictionary<string, TDriveStatus>;
  127. FConfirmDelete: Boolean;
  128. FConfirmOverwrite: Boolean;
  129. FWatchDirectory: Boolean;
  130. FDirectory: string;
  131. FShowVolLabel: Boolean;
  132. FVolDisplayStyle: TVolumeDisplayStyle;
  133. FChangeFlag: Boolean;
  134. FLastDir: string;
  135. FValidateFlag: Boolean;
  136. FSysColorChangePending: Boolean;
  137. FCreating: Boolean;
  138. FForceRename: Boolean;
  139. FRenameNode: TTreeNode;
  140. FLastRenameName: string;
  141. FInternalWindowHandle: HWND;
  142. FChangeNotify: ULONG;
  143. FPrevSelected: TTreeNode;
  144. FPrevSelectedIndex: Integer;
  145. FChangeTimerSuspended: Integer;
  146. FSubDirReaderThread: TSubDirReaderThread;
  147. FDelayedNodes: TStringList;
  148. FDelayedNodeTimer: TTimer;
  149. FRecreateScheduledCount: Integer;
  150. {Additional events:}
  151. FOnDisplayContextMenu: TNotifyEvent;
  152. FOnRefreshDrives: TDriveViewRefreshDrives;
  153. FOnNeedHiddenDirectories: TNotifyEvent;
  154. {used components:}
  155. FDirView: TDirView;
  156. FFileOperator: TFileOperator;
  157. FChangeInterval: Cardinal;
  158. {Drag&drop:}
  159. FLastPathCut: string;
  160. {Drag&drop helper functions:}
  161. procedure SignalDirDelete(Sender: TObject; Files: TStringList);
  162. function GetSubDir(var SRec: TSearchRec): Boolean;
  163. function FindFirstSubDir(Path: string; var SRec: TSearchRec): Boolean;
  164. function FindNextSubDir(var SRec: TSearchRec): Boolean;
  165. procedure ReadSubDirs(Node: TTreeNode);
  166. procedure CancelDelayedNode(Node: TTreeNode);
  167. procedure DelayedNodeTimer(Sender: TObject);
  168. function ReadSubDirsBatch(Node: TTreeNode; var SRec: TSearchRec; CheckInterval, Limit: Integer): Boolean;
  169. procedure UpdateDelayedNodeTimer;
  170. {Callback-functions used by iteratesubtree:}
  171. function CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  172. procedure DeleteNode(Node: TTreeNode);
  173. { Notification procedures used by component TDiscMonitor: }
  174. procedure ChangeDetected(Sender: TObject; const Directory: string;
  175. var SubdirsChanged: Boolean);
  176. procedure ChangeInvalid(Sender: TObject; const Directory: string; const ErrorStr: string);
  177. {Notification procedure used by component TTimer:}
  178. procedure ChangeTimerOnTimer(Sender: TObject);
  179. protected
  180. procedure SetSelected(Node: TTreeNode);
  181. procedure SetWatchDirectory(Value: Boolean);
  182. procedure SetShowVolLabel(ShowIt: Boolean);
  183. procedure SetDirView(Value: TDirView);
  184. procedure SetDirectory(Value: string); override;
  185. function DoScanDir(FromNode: TTreeNode): Boolean;
  186. procedure AddChildNode(ParentNode: TTreeNode; ParentPath: string; SRec: TSearchRec);
  187. procedure CreateWatchThread(Drive: string);
  188. function NodeWatched(Node: TTreeNode): Boolean;
  189. procedure TerminateWatchThread(Drive: string);
  190. function WatchThreadActive: Boolean; overload;
  191. function WatchThreadActive(Drive: string): Boolean; overload;
  192. procedure InternalWndProc(var Msg: TMessage);
  193. procedure UpdateDriveNotifications(Drive: string);
  194. procedure DriveRemoved(Drive: string);
  195. procedure DriveRemoving(Drive: string);
  196. procedure CancelDriveRefresh;
  197. procedure DoRefreshDrives(Global: Boolean);
  198. procedure RefreshRootNodes(dsFlags: Integer);
  199. function DirAttrMask: Integer;
  200. function CreateDriveStatus: TDriveStatus;
  201. procedure ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  202. NewDirs: Boolean); override;
  203. procedure RebuildTree; override;
  204. procedure SetLastPathCut(Path: string);
  205. function GetCanUndoCopyMove: Boolean; virtual;
  206. procedure CreateWnd; override;
  207. procedure DestroyWnd; override;
  208. procedure Edit(const Item: TTVItem); override;
  209. procedure WMUserRename(var Message: TMessage); message WM_USER_RENAME;
  210. procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE;
  211. function GetCustomDirView: TCustomDirView; override;
  212. procedure SetCustomDirView(Value: TCustomDirView); override;
  213. function NodePath(Node: TTreeNode): string; override;
  214. function NodeIsRecycleBin(Node: TTreeNode): Boolean; override;
  215. function NodePathExists(Node: TTreeNode): Boolean; override;
  216. function NodeColor(Node: TTreeNode): TColor; override;
  217. function FindPathNode(Path: string): TTreeNode; override;
  218. function DoFindNodeToPath(Path: string; ExistingOnly: Boolean): TTreeNode;
  219. function CreateNode: TTreeNode; override;
  220. function DDSourceEffects: TDropEffectSet; override;
  221. procedure DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer); override;
  222. function DragCompleteFileList: Boolean; override;
  223. function DDExecute: TDragResult; override;
  224. procedure PasteFromClipBoard(Node: TTreeNode);
  225. public
  226. property Images;
  227. property StateImages;
  228. property Items stored False;
  229. property Selected Write SetSelected stored False;
  230. property DragImageList: TDragImageList read FDragImageList;
  231. property CanUndoCopyMove: Boolean read GetCanUndoCopyMove;
  232. property DDFileOperator: TFileOperator read FFileOperator;
  233. property LastPathCut: string read FLastPathCut write SetLastPathCut;
  234. function UndoCopyMove: Boolean; dynamic;
  235. procedure EmptyClipboard; dynamic;
  236. function CopyToClipBoard(Node: TTreeNode): Boolean; dynamic;
  237. function CutToClipBoard(Node: TTreeNode): Boolean; dynamic;
  238. function CanPasteFromClipBoard: Boolean; dynamic;
  239. procedure PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer); override;
  240. {Drive handling:}
  241. function GetDriveStatus(Drive: string): TDriveStatus;
  242. function GetDriveTypetoNode(Node: TTreeNode): Integer; {Returns DRIVE_CDROM etc..}
  243. function GetDriveToNode(Node: TTreeNode): string;
  244. function GetDriveText(Drive: string): string;
  245. procedure ScanDrive(Drive: string);
  246. function GetDrives: TStrings;
  247. procedure ScheduleDriveRefresh;
  248. {Node handling:}
  249. procedure SetImageIndex(Node: TTreeNode); virtual;
  250. function FindNodeToPath(Path: string): TTreeNode;
  251. function TryFindNodeToPath(Path: string): TTreeNode;
  252. function RootNode(Node: TTreeNode): TTreeNode;
  253. function GetDirName(Node: TTreeNode): string;
  254. function GetDisplayName(Node: TTreeNode): string;
  255. function NodePathName(Node: TTreeNode): string; override;
  256. constructor Create(AOwner: TComponent); override;
  257. destructor Destroy; override;
  258. {Menu-handling:}
  259. procedure DisplayContextMenu(Node: TTreeNode; Point: TPoint); override;
  260. procedure DisplayPropertiesMenu(Node: TTreeNode); override;
  261. {Watchthread handling:}
  262. procedure StartWatchThread;
  263. procedure StopWatchThread;
  264. procedure SuspendChangeTimer;
  265. procedure ResumeChangeTimer;
  266. procedure StartAllWatchThreads;
  267. procedure StopAllWatchThreads;
  268. procedure ValidateCurrentDirectoryIfNotMonitoring;
  269. (* Modified Events: *)
  270. procedure GetImageIndex(Node: TTreeNode); override;
  271. function CanEdit(Node: TTreeNode): Boolean; override;
  272. function CanChange(Node: TTreeNode): Boolean; override;
  273. function CanExpand(Node: TTreeNode): Boolean; override;
  274. procedure Delete(Node: TTreeNode); override;
  275. procedure Loaded; override;
  276. procedure KeyPress(var Key: Char); override;
  277. procedure Change(Node: TTreeNode); override;
  278. published
  279. {Additional properties:}
  280. {Current selected directory:}
  281. property Directory;
  282. {Confirm deleting directories:}
  283. property ConfirmDelete: Boolean read FConfirmDelete write FConfirmDelete default True;
  284. {Confirm overwriting directories:}
  285. property ConfirmOverwrite: Boolean read FConfirmOverwrite write FConfirmOverwrite default True;
  286. {Enable automatic update on filesystem changes:}
  287. property WatchDirectory: Boolean read FWatchDirectory write SetWatchDirectory default False;
  288. {Linked component TDirView:}
  289. property DirView: TDirView read FDirView write SetDirView;
  290. {Show the volume labels of drives:}
  291. property ShowVolLabel: Boolean read FShowVolLabel write SetShowVolLabel default True;
  292. {Additional events:}
  293. property OnDisplayContextMenu: TNotifyEvent read FOnDisplayContextMenu
  294. write FOnDisplayContextMenu;
  295. property OnRefreshDrives: TDriveViewRefreshDrives read FOnRefreshDrives write FOnRefreshDrives;
  296. property OnBusy;
  297. property DDLinkOnExeDrag;
  298. property TargetPopUpMenu;
  299. property OnDDDragEnter;
  300. property OnDDDragLeave;
  301. property OnDDDragOver;
  302. property OnDDDrop;
  303. property OnDDQueryContinueDrag;
  304. property OnDDGiveFeedback;
  305. property OnDDDragDetect;
  306. property OnDDProcessDropped;
  307. property OnDDError;
  308. property OnDDExecuted;
  309. property OnDDFileOperation;
  310. property OnDDFileOperationExecuted;
  311. property Align;
  312. property Anchors;
  313. property AutoExpand;
  314. property BiDiMode;
  315. property BorderStyle;
  316. property BorderWidth;
  317. property ChangeDelay;
  318. property Color;
  319. property Ctl3D;
  320. property Constraints;
  321. property DoubleBuffered;
  322. {Delphi's drag&drop is not compatible with the OLE windows drag&drop:}
  323. property DragKind;
  324. property DragCursor;
  325. property DragMode Default dmAutomatic;
  326. property OnDragDrop;
  327. property OnDragOver;
  328. property Enabled;
  329. property Font;
  330. property HideSelection;
  331. property HotTrack;
  332. property Indent;
  333. property ParentBiDiMode;
  334. property ParentColor;
  335. property ParentCtl3D;
  336. property ParentDoubleBuffered;
  337. property ParentFont;
  338. property ParentShowHint;
  339. property PopupMenu;
  340. property ReadOnly;
  341. property RightClickSelect;
  342. property RowSelect;
  343. property ShowButtons;
  344. property ShowHint;
  345. property ShowLines;
  346. property TabOrder;
  347. property TabStop default True;
  348. property ToolTips;
  349. property Visible;
  350. property OnChange;
  351. property OnChanging;
  352. property OnClick;
  353. property OnCollapsing;
  354. property OnCollapsed;
  355. property OnCompare;
  356. property OnDblClick;
  357. property OnDeletion;
  358. property OnEdited;
  359. property OnEditing;
  360. property OnEndDock;
  361. property OnEndDrag;
  362. property OnEnter;
  363. property OnExit;
  364. property OnExpanding;
  365. property OnExpanded;
  366. property OnGetImageIndex;
  367. property OnGetSelectedIndex;
  368. property OnKeyDown;
  369. property OnKeyPress;
  370. property OnKeyUp;
  371. property OnMouseDown;
  372. property OnMouseMove;
  373. property OnMouseUp;
  374. property OnStartDock;
  375. property OnStartDrag;
  376. property OnNeedHiddenDirectories: TNotifyEvent read FOnNeedHiddenDirectories write FOnNeedHiddenDirectories;
  377. end;
  378. var
  379. DriveViewLoadingTooLongLimit: Integer = 0;
  380. procedure Register;
  381. implementation
  382. uses
  383. PasTools, UITypes, SyncObjs, IOUtils, System.DateUtils;
  384. type
  385. PInt = ^Integer;
  386. procedure Register;
  387. begin
  388. RegisterComponents('DriveDir', [TDriveView]);
  389. end; {Register}
  390. constructor TNodeData.Create;
  391. begin
  392. inherited;
  393. FAttr := 0;
  394. FScanned := False;
  395. FDirName := '';
  396. FIsRecycleBin := False;
  397. FIconEmpty := True;
  398. FSchedule := nil;
  399. DelayedExclude := nil;
  400. end; {TNodeData.Create}
  401. destructor TNodeData.Destroy;
  402. begin
  403. Assert(not Assigned(FSchedule));
  404. SetLength(FDirName, 0);
  405. inherited;
  406. end; {TNodeData.Destroy}
  407. { TSubDirReaderThread }
  408. constructor TSubDirReaderThread.Create(DriveView: TDriveView);
  409. begin
  410. inherited Create(True);
  411. FDriveView := DriveView;
  412. FSection.Initialize;
  413. FEvent := CreateEvent(nil, False, False, nil);
  414. FQueue := TStack<TSubDirReaderSchedule>.Create;
  415. FResults := TQueue<TSubDirReaderSchedule>.Create;
  416. FTimer := TTimer.Create(FDriveView);
  417. FTimer.Enabled := False;
  418. FTimer.Interval := 200;
  419. FTimer.OnTimer := Timer;
  420. FWindowHandle := AllocateHWnd(WndProc);
  421. end;
  422. destructor TSubDirReaderThread.Destroy;
  423. procedure DestroyScheduleList(List: TEnumerable<TSubDirReaderSchedule>);
  424. var
  425. Schedule: TSubDirReaderSchedule;
  426. begin
  427. for Schedule in List do
  428. begin
  429. if not Schedule.Deleted then
  430. TNodeData(Schedule.Node.Data).Schedule := nil;
  431. Schedule.Free;
  432. end;
  433. List.Destroy;
  434. end;
  435. begin
  436. inherited;
  437. DeallocateHWnd(FWindowHandle);
  438. DestroyScheduleList(FQueue);
  439. DestroyScheduleList(FResults);
  440. CloseHandle(FEvent);
  441. FTimer.Destroy;
  442. FSection.Destroy;
  443. end;
  444. procedure TSubDirReaderThread.WndProc(var Msg: TMessage);
  445. begin
  446. if Msg.Msg = WM_USER_SUBDIRREADER then
  447. ScheduleProcess
  448. else
  449. Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam);
  450. end;
  451. procedure TSubDirReaderThread.Process;
  452. var
  453. Started: DWORD;
  454. Elapsed: Integer;
  455. Later: Boolean;
  456. begin
  457. Started := GetTickCount;
  458. Later := False;
  459. while (not Later) and ProcessResult do
  460. begin
  461. Elapsed := GetTickCount - Started;
  462. Later := (Elapsed < 0) or (Elapsed > 20);
  463. end;
  464. if not Later then
  465. FTimer.Enabled := False;
  466. end;
  467. procedure TSubDirReaderThread.Timer(Sender: TObject);
  468. begin
  469. Process;
  470. end;
  471. procedure TSubDirReaderThread.Add(Node: TTreeNode; Path: string);
  472. var
  473. NodeData: TNodeData;
  474. Schedule: TSubDirReaderSchedule;
  475. begin
  476. if Suspended then
  477. Resume;
  478. FSection.Enter;
  479. try
  480. NodeData := TNodeData(Node.Data);
  481. Assert(not Assigned(NodeData.Schedule));
  482. Schedule := TSubDirReaderSchedule.Create;
  483. Schedule.Node := Node;
  484. Schedule.Path := Path;
  485. Schedule.Deleted := False;
  486. Schedule.Processed := False;
  487. FQueue.Push(Schedule);
  488. NodeData.Schedule := Schedule;
  489. finally
  490. FSection.Leave;
  491. end;
  492. TriggerEvent;
  493. end;
  494. procedure TSubDirReaderThread.Delete(Node: TTreeNode);
  495. var
  496. NodeData: TNodeData;
  497. begin
  498. FSection.Enter;
  499. try
  500. NodeData := TNodeData(Node.Data);
  501. if Assigned(NodeData.Schedule) then
  502. begin
  503. NodeData.Schedule.Deleted := True;
  504. NodeData.Schedule := nil;
  505. end;
  506. finally
  507. FSection.Leave;
  508. end;
  509. TriggerEvent;
  510. end;
  511. function TSubDirReaderThread.Detach: Integer;
  512. procedure DetachList(List: TEnumerable<TSubDirReaderSchedule>);
  513. var
  514. Schedule: TSubDirReaderSchedule;
  515. begin
  516. for Schedule in List do
  517. begin
  518. if Schedule.Deleted then Schedule.Free
  519. else
  520. begin
  521. Assert(Schedule.Processed = (List = FResults));
  522. Schedule.Node := nil;
  523. Inc(Result);
  524. end;
  525. end;
  526. end;
  527. begin
  528. // block thread while handle is being recreated
  529. FSection.Enter;
  530. try
  531. Result := 0;
  532. DetachList(FQueue);
  533. DetachList(FResults);
  534. FQueue.Clear;
  535. FResults.Clear;
  536. except
  537. FSection.Leave;
  538. raise;
  539. end;
  540. end;
  541. procedure TSubDirReaderThread.Reattach(Count: Integer);
  542. var
  543. Node: TTreeNode;
  544. Schedule: TSubDirReaderSchedule;
  545. begin
  546. try
  547. if Count > 0 then
  548. begin
  549. Node := FDriveView.Items.GetFirstNode;
  550. while Assigned(Node) do
  551. begin
  552. Schedule := TNodeData(Node.Data).Schedule;
  553. if Assigned(Schedule) then
  554. begin
  555. Assert(not Assigned(Schedule.Node));
  556. Schedule.Node := Node;
  557. if not Schedule.Processed then
  558. FQueue.Push(Schedule)
  559. else
  560. FResults.Enqueue(Schedule);
  561. Assert(Count > 0);
  562. // Can be optimized to stop once Count = 0
  563. Dec(Count);
  564. end;
  565. Node := Node.GetNext;
  566. end;
  567. if Count <> 0 then Assert(False); // shut up
  568. end;
  569. finally
  570. FSection.Leave;
  571. end;
  572. TriggerEvent;
  573. ScheduleProcess;
  574. end;
  575. procedure TSubDirReaderThread.Terminate;
  576. begin
  577. inherited;
  578. TriggerEvent;
  579. end;
  580. procedure TSubDirReaderThread.TriggerEvent;
  581. begin
  582. SetEvent(FEvent);
  583. end;
  584. function TSubDirReaderThread.ProcessResult: Boolean;
  585. var
  586. Node: TTreeNode;
  587. NodeData: TNodeData;
  588. Schedule: TSubDirReaderSchedule;
  589. begin
  590. FSection.Enter;
  591. try
  592. Result := (FResults.Count > 0);
  593. if Result then
  594. begin
  595. Schedule := FResults.Dequeue;
  596. if not Schedule.Deleted then
  597. begin
  598. Assert(Schedule.Processed);
  599. Node := Schedule.Node;
  600. Node.HasChildren := False;
  601. NodeData := TNodeData(Node.Data);
  602. NodeData.Scanned := not Node.HasChildren; // = True
  603. Assert(NodeData.Schedule = Schedule);
  604. NodeData.Schedule := nil;
  605. end;
  606. Schedule.Free;
  607. end;
  608. finally
  609. FSection.Leave;
  610. end;
  611. end;
  612. procedure TSubDirReaderThread.ScheduleProcess;
  613. begin
  614. // process the first batch immediatelly, to make it more likely that the first seen subdirectories
  615. // will immediatelly show correct status
  616. Process;
  617. FTimer.Enabled := True;
  618. end;
  619. procedure TSubDirReaderThread.Execute;
  620. var
  621. SRec: TSearchRec;
  622. HasSubDirs: Boolean;
  623. NodeData: TNodeData;
  624. Schedule: TSubDirReaderSchedule;
  625. DelayStart, DelayStartStep: Integer;
  626. begin
  627. DelayStart := 3000;
  628. DelayStartStep := 100;
  629. while (DelayStart > 0) and (not Terminated) do
  630. begin
  631. Sleep(DelayStartStep);
  632. Dec(DelayStart, DelayStartStep)
  633. end;
  634. while not Terminated do
  635. begin
  636. WaitForSingleObject(FEvent, INFINITE);
  637. while not Terminated do
  638. begin
  639. FSection.Enter;
  640. try
  641. if FQueue.Count = 0 then
  642. begin
  643. Break;
  644. end
  645. else
  646. begin
  647. Schedule := FQueue.Pop;
  648. if Schedule.Deleted then
  649. begin
  650. Schedule.Free;
  651. // Can be optimized to loop within locked critical section until first non-deleted schedule is found
  652. Continue;
  653. end;
  654. Assert(not Schedule.Processed);
  655. end
  656. finally
  657. FSection.Leave;
  658. end;
  659. HasSubDirs := FDriveView.FindFirstSubDir(IncludeTrailingBackslash(Schedule.Path) + '*.*', SRec);
  660. FindClose(SRec);
  661. FSection.Enter;
  662. try
  663. if Schedule.Deleted then
  664. begin
  665. Schedule.Free;
  666. end
  667. else
  668. begin
  669. Schedule.Processed := True;
  670. if not HasSubDirs then // optimization
  671. begin
  672. FResults.Enqueue(Schedule);
  673. if FResults.Count = 1 then
  674. PostMessage(FWindowHandle, WM_USER_SUBDIRREADER, 0, 0);
  675. end
  676. else
  677. begin
  678. // can happen only if the tree handle is just being recreated
  679. if Assigned(Schedule.Node) then
  680. begin
  681. NodeData := TNodeData(Schedule.Node.Data);
  682. NodeData.Schedule := nil;
  683. end;
  684. Schedule.Free;
  685. end;
  686. end;
  687. finally
  688. FSection.Leave;
  689. end;
  690. end;
  691. end;
  692. end;
  693. { TDriveTreeNode }
  694. // Not sure if this is ever used (possibly only then "assigning" tree view to another instance, what never do).
  695. // It is NOT used when recreating a tree view handle - for that a node is serialized and deserialized,
  696. // including a pointer to TNodeData. See csRecreating condition in TDriveView.Delete.
  697. procedure TDriveTreeNode.Assign(Source: TPersistent);
  698. var
  699. SourceData: TNodeData;
  700. NewData: TNodeData;
  701. begin
  702. Assert(False);
  703. inherited Assign(Source);
  704. if not Deleting and (Source is TTreeNode) then
  705. begin
  706. SourceData := TNodeData(TTreeNode(Source).Data);
  707. NewData := TNodeData.Create();
  708. NewData.DirName := SourceData.DirName;
  709. NewData.Attr := SourceData.Attr;
  710. NewData.Scanned := SourceData.Scanned;
  711. NewData.Data := SourceData.Data;
  712. NewData.FIsRecycleBin := SourceData.FIsRecycleBin;
  713. NewData.IconEmpty := SourceData.IconEmpty;
  714. TTreeNode(Source).Data := NewData;
  715. end;
  716. end;
  717. { TDriveView }
  718. constructor TDriveView.Create(AOwner: TComponent);
  719. var
  720. Drive: TRealDrive;
  721. ChangeNotifyEntry: TSHChangeNotifyEntry;
  722. Dummy: string;
  723. begin
  724. inherited;
  725. FCreating := True;
  726. FDriveStatus := TObjectDictionary<string, TDriveStatus>.Create([doOwnsValues]);
  727. FChangeInterval := MSecsPerSec;
  728. for Drive := FirstDrive to LastDrive do
  729. begin
  730. FDriveStatus.Add(Drive, CreateDriveStatus);
  731. end;
  732. FFileOperator := TFileOperator.Create(Self);
  733. FSubDirReaderThread := TSubDirReaderThread.Create(Self);
  734. FDelayedNodes := TStringList.Create;
  735. FDelayedNodeTimer := TTimer.Create(Self);
  736. UpdateDelayedNodeTimer;
  737. FDelayedNodeTimer.Interval := 250;
  738. FDelayedNodeTimer.OnTimer := DelayedNodeTimer;
  739. FShowVolLabel := True;
  740. FChangeFlag := False;
  741. FLastDir := EmptyStr;
  742. FValidateFlag := False;
  743. FSysColorChangePending := False;
  744. FConfirmDelete := True;
  745. FDirectory := EmptyStr;
  746. FForceRename := False;
  747. FLastRenameName := '';
  748. FRenameNode := nil;
  749. FPrevSelected := nil;
  750. FPrevSelectedIndex := -1;
  751. FChangeTimerSuspended := 0;
  752. FRecreateScheduledCount := -1;
  753. FConfirmOverwrite := True;
  754. FLastPathCut := '';
  755. FStartPos.X := -1;
  756. FStartPos.Y := -1;
  757. FDragPos := FStartPos;
  758. FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
  759. // Source: petr.solin 2022-02-25
  760. FChangeNotify := 0;
  761. if SpecialFolderLocation(CSIDL_DESKTOP, Dummy, ChangeNotifyEntry.pidl) then
  762. begin
  763. ChangeNotifyEntry.fRecursive := False;
  764. FChangeNotify :=
  765. SHChangeNotifyRegister(
  766. FInternalWindowHandle, SHCNRF_ShellLevel or SHCNRF_NewDelivery,
  767. SHCNE_RENAMEFOLDER or SHCNE_MEDIAINSERTED or SHCNE_MEDIAREMOVED,
  768. WM_USER_SHCHANGENOTIFY, 1, ChangeNotifyEntry);
  769. end;
  770. with FDragDropFilesEx do
  771. begin
  772. ShellExtensions.DragDropHandler := True;
  773. end;
  774. end; {Create}
  775. destructor TDriveView.Destroy;
  776. var
  777. DriveStatusPair: TDriveStatusPair;
  778. begin
  779. if FChangeNotify <> 0 then SHChangeNotifyDeregister(FChangeNotify);
  780. Classes.DeallocateHWnd(FInternalWindowHandle);
  781. for DriveStatusPair in FDriveStatus do
  782. begin
  783. with DriveStatusPair.Value do
  784. begin
  785. if Assigned(DiscMonitor) then
  786. FreeAndNil(DiscMonitor);
  787. if Assigned(ChangeTimer) then
  788. FreeAndNil(ChangeTimer);
  789. end;
  790. UpdateDriveNotifications(DriveStatusPair.Key);
  791. end;
  792. FDriveStatus.Free;
  793. if Assigned(FFileOperator) then
  794. FFileOperator.Free;
  795. FSubDirReaderThread.Free;
  796. Assert(FDelayedNodes.Count = 0);
  797. FreeAndNil(FDelayedNodes);
  798. inherited Destroy;
  799. end; {Destroy}
  800. function TDriveView.CreateDriveStatus: TDriveStatus;
  801. begin
  802. Result := TDriveStatus.Create;
  803. with Result do
  804. begin
  805. Scanned := False;
  806. Verified := False;
  807. RootNode := nil;
  808. RootNodeIndex := -1;
  809. DiscMonitor := nil;
  810. DefaultDir := EmptyStr;
  811. {ChangeTimer: }
  812. ChangeTimer := TTimer.Create(Self);
  813. ChangeTimer.Interval := 0;
  814. ChangeTimer.Enabled := False;
  815. ChangeTimer.OnTimer := ChangeTimerOnTimer;
  816. DriveHandle := INVALID_HANDLE_VALUE;
  817. NotificationHandle := nil;
  818. end;
  819. end;
  820. procedure TDriveView.DriveRemoving(Drive: string);
  821. begin
  822. DriveRemoved(Drive);
  823. TerminateWatchThread(Drive);
  824. end;
  825. type
  826. PDevBroadcastHdr = ^TDevBroadcastHdr;
  827. TDevBroadcastHdr = record
  828. dbch_size: DWORD;
  829. dbch_devicetype: DWORD;
  830. dbch_reserved: DWORD;
  831. end;
  832. PDevBroadcastVolume = ^TDevBroadcastVolume;
  833. TDevBroadcastVolume = record
  834. dbcv_size: DWORD;
  835. dbcv_devicetype: DWORD;
  836. dbcv_reserved: DWORD;
  837. dbcv_unitmask: DWORD;
  838. dbcv_flags: WORD;
  839. end;
  840. PDEV_BROADCAST_HANDLE = ^DEV_BROADCAST_HANDLE;
  841. DEV_BROADCAST_HANDLE = record
  842. dbch_size : DWORD;
  843. dbch_devicetype : DWORD;
  844. dbch_reserved : DWORD;
  845. dbch_handle : THandle;
  846. dbch_hdevnotify : HDEVNOTIFY ;
  847. dbch_eventguid : TGUID;
  848. dbch_nameoffset : LongInt;
  849. dbch_data : Byte;
  850. end;
  851. PPItemIDList = ^PItemIDList;
  852. const
  853. DBT_DEVTYP_HANDLE = $00000006;
  854. DBT_CONFIGCHANGED = $0018;
  855. DBT_DEVICEARRIVAL = $8000;
  856. DBT_DEVICEQUERYREMOVE = $8001;
  857. DBT_DEVICEREMOVEPENDING = $8003;
  858. DBT_DEVICEREMOVECOMPLETE = $8004;
  859. DBT_DEVTYP_VOLUME = $00000002;
  860. // WORKAROUND Declaration in Winapi.ShlObj.pas is wrong
  861. function SHChangeNotification_Lock(hChange: THandle; dwProcId: DWORD;
  862. var PPidls: PPItemIDList; var plEvent: Longint): THANDLE; stdcall;
  863. external 'shell32.dll' name 'SHChangeNotification_Lock';
  864. procedure TDriveView.InternalWndProc(var Msg: TMessage);
  865. var
  866. DeviceType: DWORD;
  867. UnitMask: DWORD;
  868. DeviceHandle: THandle;
  869. Drive: Char;
  870. DriveStatusPair: TDriveStatusPair;
  871. PPIDL: PPItemIDList;
  872. Event: LONG;
  873. Lock: THandle;
  874. begin
  875. with Msg do
  876. begin
  877. if Msg = WM_USER_SHCHANGENOTIFY then
  878. begin
  879. Lock := SHChangeNotification_Lock(wParam, lParam, PPIDL, Event);
  880. try
  881. if (Event = SHCNE_RENAMEFOLDER) or // = drive rename
  882. (Event = SHCNE_MEDIAINSERTED) or // also bitlocker drive unlock (also sends SHCNE_UPDATEDIR)
  883. (Event = SHCNE_MEDIAREMOVED) then
  884. begin
  885. ScheduleDriveRefresh;
  886. end;
  887. finally
  888. SHChangeNotification_Unlock(Lock);
  889. end;
  890. end
  891. else
  892. if Msg = WM_DEVICECHANGE then
  893. begin
  894. if (wParam = DBT_CONFIGCHANGED) or
  895. (wParam = DBT_DEVICEARRIVAL) or
  896. (wParam = DBT_DEVICEREMOVECOMPLETE) then
  897. begin
  898. ScheduleDriveRefresh;
  899. end
  900. else
  901. if (wParam = DBT_DEVICEQUERYREMOVE) or
  902. (wParam = DBT_DEVICEREMOVEPENDING) then
  903. begin
  904. DeviceType := PDevBroadcastHdr(lParam)^.dbch_devicetype;
  905. // This is specifically for VeraCrypt.
  906. // For normal drives, see DBT_DEVTYP_HANDLE below
  907. // (and maybe now that we have generic implementation, this specific code for VeraCrypt might not be needed anymore)
  908. if DeviceType = DBT_DEVTYP_VOLUME then
  909. begin
  910. UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
  911. Drive := FirstDrive;
  912. while UnitMask > 0 do
  913. begin
  914. if UnitMask and $01 <> 0 then
  915. begin
  916. DriveRemoving(Drive);
  917. end;
  918. UnitMask := UnitMask shr 1;
  919. Drive := Chr(Ord(Drive) + 1);
  920. end;
  921. end
  922. else
  923. if DeviceType = DBT_DEVTYP_HANDLE then
  924. begin
  925. DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
  926. for DriveStatusPair in FDriveStatus do
  927. if DriveStatusPair.Value.DriveHandle = DeviceHandle then
  928. begin
  929. DriveRemoving(DriveStatusPair.Key);
  930. end;
  931. end;
  932. end;
  933. end
  934. else
  935. if Msg = WM_TIMER then
  936. begin
  937. CancelDriveRefresh;
  938. try
  939. //DriveInfo.Load;
  940. RefreshRootNodes(dsAll or dvdsRereadAllways);
  941. DoRefreshDrives(True);
  942. except
  943. Application.HandleException(Self);
  944. end;
  945. end;
  946. Result := DefWindowProc(FInternalWindowHandle, Msg, wParam, lParam);
  947. end;
  948. end;
  949. procedure TDriveView.DoRefreshDrives(Global: Boolean);
  950. begin
  951. if Assigned(OnRefreshDrives) then
  952. OnRefreshDrives(Self, Global);
  953. end;
  954. procedure TDriveView.CancelDriveRefresh;
  955. begin
  956. KillTimer(FInternalWindowHandle, 1);
  957. end;
  958. procedure TDriveView.ScheduleDriveRefresh;
  959. begin
  960. CancelDriveRefresh;
  961. // Delay refreshing drives for a sec.
  962. // Particularly with CD/DVD drives, if we query display name
  963. // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
  964. // Actually one sec does not help usually, but we do not want to wait any longer,
  965. // because we want to add USB drives asap.
  966. // And this problem might be solved now by SHChangeNotifyRegister/SHCNE_RENAMEFOLDER.
  967. SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
  968. end;
  969. procedure TDriveView.CreateWnd;
  970. var
  971. DriveStatus: TDriveStatus;
  972. begin
  973. inherited;
  974. FDragDropFilesEx.SourceEffects := [deCopy, deMove, deLink];
  975. FDragDropFilesEx.TargetEffects := [deCopy, deMove, deLink];
  976. if FPrevSelectedIndex >= 0 then
  977. begin
  978. FPrevSelected := Items[FPrevSelectedIndex];
  979. FPrevSelectedIndex := -1;
  980. end;
  981. for DriveStatus in FDriveStatus.Values do
  982. with DriveStatus do
  983. begin
  984. if RootNodeIndex >= 0 then
  985. begin
  986. RootNode := Items[RootNodeIndex];
  987. RootNodeIndex := -1;
  988. end;
  989. end;
  990. UpdateDelayedNodeTimer;
  991. if FRecreateScheduledCount >= 0 then
  992. begin
  993. FSubDirReaderThread.Reattach(FRecreateScheduledCount);
  994. FRecreateScheduledCount := -1;
  995. end;
  996. end; {CreateWnd}
  997. procedure TDriveView.DestroyWnd;
  998. var
  999. DriveStatus: TDriveStatus;
  1000. I: Integer;
  1001. begin
  1002. FDelayedNodeTimer.Enabled := False;
  1003. for I := 0 to FDelayedNodes.Count - 1 do
  1004. FDelayedNodes.Objects[I] := nil;
  1005. if not (csRecreating in ControlState) then
  1006. begin
  1007. FSubDirReaderThread.Terminate;
  1008. FSubDirReaderThread.WaitFor;
  1009. end
  1010. else
  1011. if CreateWndRestores then
  1012. begin
  1013. Assert(FRecreateScheduledCount < 0);
  1014. // Have to use field, instead of local variable in CM_RECREATEWND handler,
  1015. // as CM_RECREATEWND is not invoked, when the recreation is trigerred recursivelly from parent
  1016. // control/form.
  1017. FRecreateScheduledCount := FSubDirReaderThread.Detach;
  1018. if Items.Count > 0 then // redundant test?
  1019. begin
  1020. FPrevSelectedIndex := -1;
  1021. if Assigned(FPrevSelected) then
  1022. begin
  1023. FPrevSelectedIndex := FPrevSelected.AbsoluteIndex;
  1024. FPrevSelected := nil;
  1025. end;
  1026. for DriveStatus in FDriveStatus.Values do
  1027. with DriveStatus do
  1028. begin
  1029. RootNodeIndex := -1;
  1030. if Assigned(RootNode) then
  1031. begin
  1032. RootNodeIndex := RootNode.AbsoluteIndex;
  1033. RootNode := nil;
  1034. end;
  1035. end;
  1036. end;
  1037. end;
  1038. inherited;
  1039. end;
  1040. function TDriveView.NodeColor(Node: TTreeNode): TColor;
  1041. begin
  1042. Result := clDefaultItemColor;
  1043. with TNodeData(Node.Data) do
  1044. if not Node.Selected then
  1045. begin
  1046. {Colored display of compressed directories:}
  1047. if (Attr and FILE_ATTRIBUTE_COMPRESSED) <> 0 then
  1048. begin
  1049. if SupportsDarkMode and DarkMode then Result := clSkyBlue
  1050. else Result := clBlue;
  1051. end
  1052. else
  1053. {Dimmed display, if hidden-atrribut set:}
  1054. if FDimmHiddenDirs and ((Attr and FILE_ATTRIBUTE_HIDDEN) <> 0) then
  1055. Result := clGrayText
  1056. end;
  1057. end;
  1058. function TDriveView.GetCustomDirView: TCustomDirView;
  1059. begin
  1060. Result := DirView;
  1061. end;
  1062. procedure TDriveView.SetCustomDirView(Value: TCustomDirView);
  1063. begin
  1064. DirView := Value as TDirView;
  1065. end;
  1066. function TDriveView.NodePath(Node: TTreeNode): string;
  1067. var
  1068. ParentNode: TTreeNode;
  1069. begin
  1070. if not Assigned(Node) then
  1071. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDirPath']));
  1072. Result := GetDirName(Node);
  1073. Node := Node.Parent;
  1074. while Assigned(Node) do
  1075. begin
  1076. ParentNode := Node.Parent;
  1077. if Assigned(ParentNode) then
  1078. Result := GetDirName(Node) + '\' + Result
  1079. else
  1080. Result := GetDirName(Node) + Result;
  1081. Node := ParentNode;
  1082. end;
  1083. if IsRootPath(Result) then
  1084. Result := ExcludeTrailingBackslash(Result);
  1085. end;
  1086. {NodePathName: Returns the complete path to Node with trailing backslash on rootnodes:
  1087. C:\ ,C:\WINDOWS, C:\WINDOWS\SYSTEM }
  1088. function TDriveView.NodePathName(Node: TTreeNode): string;
  1089. begin
  1090. Result := NodePath(Node);
  1091. if IsRootPath(Result) then
  1092. Result := IncludeTrailingBackslash(Result);
  1093. end; {NodePathName}
  1094. function TDriveView.NodeIsRecycleBin(Node: TTreeNode): Boolean;
  1095. begin
  1096. Result := TNodeData(Node.Data).IsRecycleBin;
  1097. end;
  1098. function TDriveView.NodePathExists(Node: TTreeNode): Boolean;
  1099. begin
  1100. Result := DirectoryExists(ApiPath(NodePathName(Node)));
  1101. end;
  1102. function TDriveView.CanEdit(Node: TTreeNode): Boolean;
  1103. begin
  1104. Result := inherited CanEdit(Node) or FForceRename;
  1105. if Result then
  1106. begin
  1107. Result := Assigned(Node.Parent) and
  1108. (not TNodeData(Node.Data).IsRecycleBin) and
  1109. (not ReadOnly) and
  1110. (FDragDropFilesEx.DragDetectStatus <> ddsDrag) and
  1111. ((TNodeData(Node.Data).Attr and (faReadOnly or faSysFile)) = 0) and
  1112. (UpperCase(Node.Text) = UpperCase(GetDirName(Node)));
  1113. end;
  1114. FForceRename := False;
  1115. end; {CanEdit}
  1116. procedure TDriveView.Edit(const Item: TTVItem);
  1117. var
  1118. Node: TTreeNode;
  1119. Info: string;
  1120. i: Integer;
  1121. begin
  1122. Node := GetNodeFromHItem(Item);
  1123. if (Length(Item.pszText) > 0) and (Item.pszText <> Node.Text) then
  1124. begin
  1125. if StrContains(coInvalidDosChars, Item.pszText) then
  1126. begin
  1127. Info := coInvalidDosChars;
  1128. for i := Length(Info) downto 1 do
  1129. System.Insert(Space, Info, i);
  1130. if Length(Item.pszText) > 0 then
  1131. raise EInvalidDirName.Create(SErrorInvalidName + Space + Info);
  1132. Exit;
  1133. end;
  1134. StopWatchThread;
  1135. if Assigned(DirView) then
  1136. DirView.StopWatchThread;
  1137. with FFileOperator do
  1138. begin
  1139. Flags := FileOperatorDefaultFlags + [foNoConfirmation];
  1140. Operation := foRename;
  1141. OperandFrom.Clear;
  1142. OperandTo.Clear;
  1143. OperandFrom.Add(NodePath(Node));
  1144. OperandTo.Add(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText);
  1145. end;
  1146. try
  1147. if FFileOperator.Execute then
  1148. begin
  1149. Node.Text := Item.pszText;
  1150. TNodeData(Node.Data).DirName := Item.pszText;
  1151. SortChildren(Node.Parent, False);
  1152. inherited;
  1153. end
  1154. else
  1155. begin
  1156. if FileOrDirExists(IncludeTrailingBackslash(NodePath(Node.Parent)) + Item.pszText) then
  1157. Info := SErrorRenameFileExists + Item.pszText
  1158. else
  1159. Info := SErrorRenameFile + Item.pszText;
  1160. MessageBeep(MB_ICONHAND);
  1161. if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then
  1162. begin
  1163. FLastRenameName := Item.pszText;
  1164. FRenameNode := Node;
  1165. PostMessage(Self.Handle, WM_USER_RENAME, 0, 0);
  1166. end;
  1167. end;
  1168. finally
  1169. StartWatchThread;
  1170. if Assigned(DirView) then
  1171. begin
  1172. DirView.Reload2;
  1173. DirView.StartWatchThread;
  1174. end;
  1175. end;
  1176. end;
  1177. end; {Edit}
  1178. procedure TDriveView.WMUserRename(var Message: TMessage);
  1179. begin
  1180. if Assigned(FRenameNode) then
  1181. begin
  1182. FForceRename := True;
  1183. TreeView_EditLabel(Handle, FRenameNode.ItemID);
  1184. SetWindowText(TreeView_GetEditControl(Self.Handle), PChar(FLastRenameName));
  1185. FRenameNode := nil;
  1186. end;
  1187. end; {WMUserRename}
  1188. function TDriveView.CanExpand(Node: TTreeNode): Boolean;
  1189. var
  1190. SubNode: TTreeNode;
  1191. Drive: string;
  1192. SaveCursor: TCursor;
  1193. begin
  1194. Result := inherited CanExpand(Node);
  1195. Drive := GetDriveToNode(Node);
  1196. if Node.HasChildren then
  1197. begin
  1198. if (not Assigned(Node.Parent)) and
  1199. (not GetDriveStatus(Drive).Scanned) and
  1200. DriveInfo.IsFixedDrive(Drive) then
  1201. begin
  1202. SubNode := Node.GetFirstChild;
  1203. if not Assigned(SubNode) then
  1204. begin
  1205. ScanDrive(Drive);
  1206. SubNode := Node.GetFirstChild;
  1207. Node.HasChildren := Assigned(SubNode);
  1208. Result := Node.HasChildren;
  1209. if not Assigned(GetDriveStatus(Drive).DiscMonitor) then
  1210. CreateWatchThread(Drive);
  1211. end;
  1212. end
  1213. else
  1214. begin
  1215. SaveCursor := Screen.Cursor;
  1216. Screen.Cursor := crHourGlass;
  1217. try
  1218. if (not TNodeData(Node.Data).Scanned) and DoScanDir(Node) then
  1219. begin
  1220. ReadSubDirs(Node);
  1221. end;
  1222. finally
  1223. Screen.Cursor := SaveCursor;
  1224. end;
  1225. end;
  1226. end;
  1227. end; {CanExpand}
  1228. procedure TDriveView.GetImageIndex(Node: TTreeNode);
  1229. begin
  1230. if TNodeData(Node.Data).IconEmpty then
  1231. SetImageIndex(Node);
  1232. inherited;
  1233. end; {GetImageIndex}
  1234. procedure TDriveView.Loaded;
  1235. begin
  1236. inherited;
  1237. {Create the drive nodes:}
  1238. RefreshRootNodes(dsDisplayName or dvdsFloppy);
  1239. {Set the initial directory:}
  1240. if (Length(FDirectory) > 0) and DirectoryExists(ApiPath(FDirectory)) then
  1241. Directory := FDirectory;
  1242. FCreating := False;
  1243. end; {Loaded}
  1244. function TDriveView.CreateNode: TTreeNode;
  1245. begin
  1246. Result := TDriveTreeNode.Create(Items);
  1247. end;
  1248. procedure TDriveView.Delete(Node: TTreeNode);
  1249. var
  1250. NodeData: TNodeData;
  1251. begin
  1252. if Node = FPrevSelected then
  1253. FPrevSelected := nil;
  1254. NodeData := nil;
  1255. if Assigned(Node) and Assigned(Node.Data) then
  1256. NodeData := TNodeData(Node.Data);
  1257. Node.Data := nil;
  1258. inherited;
  1259. if Assigned(NodeData) and not (csRecreating in ControlState) then
  1260. begin
  1261. FSubDirReaderThread.Delete(Node);
  1262. if Assigned(NodeData.DelayedExclude) then
  1263. begin
  1264. CancelDelayedNode(Node);
  1265. FDelayedNodes.Delete(FDelayedNodes.IndexOfObject(Node));
  1266. UpdateDelayedNodeTimer;
  1267. end;
  1268. NodeData.Destroy;
  1269. end;
  1270. end; {OnDelete}
  1271. procedure TDriveView.KeyPress(var Key: Char);
  1272. begin
  1273. inherited;
  1274. if Assigned(Selected) then
  1275. begin
  1276. if Pos(Key, coInvalidDosChars) <> 0 then
  1277. begin
  1278. Beep;
  1279. Key := #0;
  1280. end;
  1281. end;
  1282. end; {KeyPress}
  1283. function TDriveView.CanChange(Node: TTreeNode): Boolean;
  1284. var
  1285. Path: string;
  1286. Drive: string;
  1287. begin
  1288. Result := inherited CanChange(Node);
  1289. if not Reading and not (csRecreating in ControlState) then
  1290. begin
  1291. if Result and Assigned(Node) then
  1292. begin
  1293. Path := NodePathName(Node);
  1294. if Path <> FLastDir then
  1295. begin
  1296. Drive := DriveInfo.GetDriveKey(Path);
  1297. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  1298. if not DriveInfo.Get(Drive).DriveReady then
  1299. begin
  1300. MessageDlg(Format(SDriveNotReady, [Drive]), mtError, [mbOK], 0);
  1301. Result := False;
  1302. end
  1303. else
  1304. try
  1305. CheckCanOpenDirectory(Path);
  1306. except
  1307. Application.HandleException(Self);
  1308. Result := False;
  1309. end;
  1310. end;
  1311. end;
  1312. if Result and (csDestroying in ComponentState) then
  1313. begin
  1314. Result := False;
  1315. end;
  1316. if Result and
  1317. (not FCanChange) and
  1318. Assigned(Node) and
  1319. Assigned(Node.Data) and
  1320. Assigned(Selected) and
  1321. Assigned(Selected.Data) then
  1322. begin
  1323. DropTarget := Node;
  1324. Result := False;
  1325. end
  1326. else
  1327. begin
  1328. DropTarget := nil;
  1329. end;
  1330. end;
  1331. end; {CanChange}
  1332. procedure TDriveView.Change(Node: TTreeNode);
  1333. var
  1334. Drive: string;
  1335. OldSerial: DWORD;
  1336. NewDir: string;
  1337. PrevDrive: string;
  1338. begin
  1339. if not Reading and not (csRecreating in ControlState) then
  1340. begin
  1341. if Assigned(Node) then
  1342. begin
  1343. NewDir := NodePathName(Node);
  1344. if NewDir <> FLastDir then
  1345. begin
  1346. Drive := DriveInfo.GetDriveKey(NewDir);
  1347. if Length(FLastDir) > 0 then
  1348. PrevDrive := DriveInfo.GetDriveKey(FLastDir)
  1349. else
  1350. PrevDrive := '';
  1351. FChangeFlag := True;
  1352. FLastDir := NewDir;
  1353. OldSerial := DriveInfo.Get(Drive).DriveSerial;
  1354. DriveInfo.ReadDriveStatus(Drive, dsSize or dsImageIndex);
  1355. with DriveInfo.Get(Drive) do
  1356. begin
  1357. if Assigned(FDirView) and (FDirView.Path <> NewDir) then
  1358. FDirView.Path := NewDir;
  1359. if DriveReady then
  1360. begin
  1361. if not DirectoryExists(ApiPath(NewDir)) then
  1362. begin
  1363. ValidateDirectory(GetDriveStatus(Drive).RootNode);
  1364. Exit;
  1365. end;
  1366. GetDriveStatus(Drive).DefaultDir := IncludeTrailingBackslash(NewDir);
  1367. if PrevDrive <> Drive then
  1368. begin
  1369. if (PrevDrive <> '') and
  1370. (DriveInfo.Get(PrevDrive).DriveType = DRIVE_REMOVABLE) then
  1371. begin
  1372. TerminateWatchThread(PrevDrive);
  1373. end;
  1374. {Drive serial has changed or is missing: allways reread the drive:}
  1375. if (DriveSerial <> OldSerial) or (DriveSerial = 0) then
  1376. begin
  1377. if TNodeData(GetDriveStatus(Drive).RootNode.Data).Scanned then
  1378. ScanDrive(Drive);
  1379. end;
  1380. end;
  1381. StartWatchThread;
  1382. end
  1383. else {Drive not ready:}
  1384. begin
  1385. GetDriveStatus(Drive).RootNode.DeleteChildren;
  1386. GetDriveStatus(Drive).DefaultDir := EmptyStr;
  1387. end;
  1388. end;
  1389. end;
  1390. if (not Assigned(FPrevSelected)) or (not FPrevSelected.HasAsParent(Node)) then
  1391. Node.Expand(False);
  1392. FPrevSelected := Node;
  1393. ValidateCurrentDirectoryIfNotMonitoring;
  1394. end;
  1395. end;
  1396. inherited;
  1397. end; {Change}
  1398. procedure TDriveView.SetImageIndex(Node: TTreeNode);
  1399. var
  1400. FileInfo: TShFileInfo;
  1401. Drive, NodePath: string;
  1402. begin
  1403. if Assigned(Node) and TNodeData(Node.Data).IconEmpty then
  1404. begin
  1405. NodePath := NodePathName(Node);
  1406. Drive := DriveInfo.GetDriveKey(NodePath);
  1407. if not Assigned(Node.Parent) then
  1408. begin
  1409. with DriveInfo.Get(Drive) do
  1410. begin
  1411. if ImageIndex = 0 then
  1412. begin
  1413. DriveInfo.ReadDriveStatus(Drive, dsImageIndex);
  1414. Node.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1415. end
  1416. else Node.ImageIndex := ImageIndex;
  1417. Node.SelectedIndex := Node.ImageIndex;
  1418. end;
  1419. end
  1420. else
  1421. begin
  1422. if DriveInfo.Get(Drive).DriveType = DRIVE_REMOTE then
  1423. begin
  1424. Node.ImageIndex := StdDirIcon;
  1425. Node.SelectedIndex := StdDirSelIcon;
  1426. end
  1427. else
  1428. begin
  1429. try
  1430. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1431. SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
  1432. if (FileInfo.iIcon < Images.Count) and (FileInfo.iIcon > 0) then
  1433. begin
  1434. Node.ImageIndex := FileInfo.iIcon;
  1435. SHGetFileInfo(PChar(NodePath), 0, FileInfo, SizeOf(FileInfo),
  1436. SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_OPENICON);
  1437. Node.SelectedIndex := FileInfo.iIcon;
  1438. end
  1439. else
  1440. begin
  1441. Node.ImageIndex := StdDirIcon;
  1442. Node.SelectedIndex := StdDirSelIcon;
  1443. end;
  1444. except
  1445. Node.ImageIndex := StdDirIcon;
  1446. Node.SelectedIndex := StdDirSelIcon;
  1447. end;
  1448. end;
  1449. end;
  1450. end; {IconEmpty}
  1451. TNodeData(Node.Data).IconEmpty := False;
  1452. end; {SetImageIndex}
  1453. function TDriveView.GetDriveText(Drive: string): string;
  1454. begin
  1455. if FShowVolLabel and (Length(DriveInfo.GetPrettyName(Drive)) > 0) then
  1456. begin
  1457. case FVolDisplayStyle of
  1458. doPrettyName: Result := DriveInfo.GetPrettyName(Drive);
  1459. doDisplayName: Result := DriveInfo.GetDisplayName(Drive);
  1460. end; {Case}
  1461. end
  1462. else
  1463. begin
  1464. Result := DriveInfo.GetSimpleName(Drive);
  1465. end;
  1466. end; {GetDriveText}
  1467. function CompareDrive(List: TStringList; Index1, Index2: Integer): Integer;
  1468. var
  1469. Drive1, Drive2: string;
  1470. RealDrive1, RealDrive2: Boolean;
  1471. begin
  1472. Drive1 := List[Index1];
  1473. Drive2 := List[Index2];
  1474. RealDrive1 := DriveInfo.IsRealDrive(Drive1);
  1475. RealDrive2 := DriveInfo.IsRealDrive(Drive2);
  1476. if RealDrive1 = RealDrive2 then
  1477. begin
  1478. Result := CompareText(Drive1, Drive2);
  1479. end
  1480. else
  1481. if RealDrive1 and (not RealDrive2) then
  1482. begin
  1483. Result := -1;
  1484. end
  1485. else
  1486. begin
  1487. Result := 1;
  1488. end;
  1489. end;
  1490. function TDriveView.GetDrives: TStrings;
  1491. var
  1492. DriveStatusPair: TDriveStatusPair;
  1493. Drives: TStringList;
  1494. begin
  1495. Drives := TStringList.Create;
  1496. { We could iterate only .Keys here, but that crashes IDE for some reason }
  1497. for DriveStatusPair in FDriveStatus do
  1498. begin
  1499. Drives.Add(DriveStatusPair.Key);
  1500. end;
  1501. Drives.CustomSort(CompareDrive);
  1502. Result := Drives;
  1503. end;
  1504. procedure TDriveView.DriveRemoved(Drive: string);
  1505. var
  1506. NewDrive: Char;
  1507. begin
  1508. if (Directory <> '') and (Directory[1] = Drive) then
  1509. begin
  1510. if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
  1511. else NewDrive := SystemDrive;
  1512. repeat
  1513. if NewDrive < SystemDrive then NewDrive := SystemDrive
  1514. else
  1515. if NewDrive = SystemDrive then NewDrive := LastDrive
  1516. else Dec(NewDrive);
  1517. DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
  1518. if NewDrive = Drive then
  1519. begin
  1520. Break;
  1521. end;
  1522. if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
  1523. begin
  1524. Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
  1525. break;
  1526. end;
  1527. until False;
  1528. if not Assigned(Selected) then
  1529. begin
  1530. Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
  1531. end;
  1532. end;
  1533. end;
  1534. procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
  1535. var
  1536. Drives: TStrings;
  1537. NewText: string;
  1538. SaveCursor: TCursor;
  1539. WasValid: Boolean;
  1540. NodeData: TNodeData;
  1541. DriveStatus: TDriveStatus;
  1542. NextDriveNode: TTreeNode;
  1543. Index: Integer;
  1544. Drive: string;
  1545. begin
  1546. SaveCursor := Screen.Cursor;
  1547. Screen.Cursor := crHourGlass;
  1548. Drives := nil;
  1549. try
  1550. Drives := GetDrives;
  1551. NextDriveNode := nil;
  1552. for Index := Drives.Count - 1 downto 0 do
  1553. begin
  1554. Drive := Drives[Index];
  1555. DriveStatus := GetDriveStatus(Drive);
  1556. if ((dsFlags and dvdsFloppy) <> 0) or DriveInfo.IsFixedDrive(Drive) then
  1557. begin
  1558. with DriveInfo.Get(Drive) do
  1559. begin
  1560. WasValid := Assigned(DriveStatus.RootNode);
  1561. end;
  1562. if ((dsFlags and dvdsReReadAllways) = 0) and
  1563. (Length(DriveInfo.Get(Drive).DisplayName) > 0) then
  1564. dsFlags := dsFlags and (not dsDisplayName);
  1565. DriveInfo.ReadDriveStatus(Drive, dsFlags);
  1566. with DriveInfo.Get(Drive), DriveStatus do
  1567. begin
  1568. if Valid then
  1569. begin
  1570. if not WasValid then
  1571. {New drive has arrived: insert new rootnode:}
  1572. begin
  1573. { Create root directory node }
  1574. NodeData := TNodeData.Create;
  1575. NodeData.DirName := DriveInfo.GetDriveRoot(Drive);
  1576. if Assigned(NextDriveNode) then
  1577. RootNode := Items.InsertObject(NextDriveNode, '', NodeData)
  1578. else
  1579. RootNode := Items.AddObject(nil, '', NodeData);
  1580. RootNode.Text := GetDisplayName(RootNode);
  1581. RootNode.HasChildren := True;
  1582. Scanned := False;
  1583. Verified := False;
  1584. end
  1585. else
  1586. if RootNode.ImageIndex <> DriveInfo.Get(Drive).ImageIndex then
  1587. begin {WasValid = True}
  1588. RootNode.ImageIndex := DriveInfo.Get(Drive).ImageIndex;
  1589. RootNode.SelectedIndex := DriveInfo.Get(Drive).ImageIndex;
  1590. end;
  1591. if Assigned(RootNode) then
  1592. begin
  1593. NewText := GetDisplayName(RootNode);
  1594. if RootNode.Text <> NewText then
  1595. RootNode.Text := NewText;
  1596. end;
  1597. end
  1598. else
  1599. if WasValid then
  1600. {Drive has been removed => delete rootnode:}
  1601. begin
  1602. DriveRemoved(Drive);
  1603. Scanned := False;
  1604. Verified := False;
  1605. RootNode.Delete;
  1606. RootNode := nil;
  1607. end;
  1608. end;
  1609. end;
  1610. if Assigned(DriveStatus.RootNode) then
  1611. NextDriveNode := DriveStatus.RootNode;
  1612. end;
  1613. finally
  1614. Screen.Cursor := SaveCursor;
  1615. Drives.Free;
  1616. end;
  1617. end; {RefreshRootNodes}
  1618. procedure TDriveView.AddChildNode(ParentNode: TTreeNode; ParentPath: string; SRec: TSearchRec);
  1619. var
  1620. NewNode: TTreeNode;
  1621. NodeData: TNodeData;
  1622. begin
  1623. NodeData := TNodeData.Create;
  1624. NodeData.Attr := SRec.Attr;
  1625. NodeData.DirName := SRec.Name;
  1626. NodeData.FIsRecycleBin :=
  1627. (SRec.Attr and faSysFile <> 0) and
  1628. (not Assigned(ParentNode.Parent)) and
  1629. (SameText(SRec.Name, 'RECYCLED') or
  1630. SameText(SRec.Name, 'RECYCLER') or
  1631. SameText(SRec.Name, '$RECYCLE.BIN'));
  1632. NodeData.Scanned := False;
  1633. NewNode := Self.Items.AddChildObject(ParentNode, '', NodeData);
  1634. NewNode.Text := GetDisplayName(NewNode);
  1635. NewNode.HasChildren := True;
  1636. if GetDriveTypeToNode(ParentNode) <> DRIVE_REMOTE then
  1637. FSubDirReaderThread.Add(NewNode, IncludeTrailingBackslash(ParentPath) + SRec.Name);
  1638. end; {AddChildNode}
  1639. function TDriveView.GetDriveStatus(Drive: string): TDriveStatus;
  1640. begin
  1641. if not FDriveStatus.TryGetValue(Drive, Result) then
  1642. begin
  1643. Result := CreateDriveStatus;
  1644. FDriveStatus.Add(Drive, Result);
  1645. RefreshRootNodes(dsAll or dvdsRereadAllways);
  1646. DoRefreshDrives(False);
  1647. end;
  1648. end; {GetDriveStatus}
  1649. function TDriveView.DoScanDir(FromNode: TTreeNode): Boolean;
  1650. begin
  1651. Result := not TNodeData(FromNode.Data).IsRecycleBin;
  1652. end; {DoScanDir}
  1653. function TDriveView.DirAttrMask: Integer;
  1654. begin
  1655. Result := faDirectory or faSysFile;
  1656. if ShowHiddenDirs then
  1657. Result := Result or faHidden;
  1658. end;
  1659. procedure TDriveView.ScanDrive(Drive: string);
  1660. begin {ScanDrive}
  1661. with Self.Items do
  1662. begin
  1663. ValidateDirectory(FindNodeToPath(DriveInfo.GetDriveRoot(Drive)));
  1664. GetDriveStatus(Drive).Scanned := True;
  1665. GetDriveStatus(Drive).Verified := False;
  1666. end;
  1667. end; {ScanDrive}
  1668. function TDriveView.DoFindNodeToPath(Path: string; ExistingOnly: Boolean): TTreeNode;
  1669. var
  1670. SelectionHierarchy: array of TTreeNode;
  1671. SelectionHierarchyHeight: Integer;
  1672. function SearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode; forward;
  1673. function ExtractFirstName(S: string): string;
  1674. var
  1675. I: Integer;
  1676. begin
  1677. I := Pos('\', S);
  1678. if I = 0 then
  1679. I := Length(S);
  1680. Result := System.Copy(S, 1, I);
  1681. end;
  1682. function DoSearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode;
  1683. var
  1684. Node: TTreeNode;
  1685. Dir: string;
  1686. begin
  1687. {Extract first directory from path:}
  1688. Dir := ExtractFirstName(Path);
  1689. System.Delete(Path, 1, Length(Dir));
  1690. if Dir[Length(Dir)] = '\' then
  1691. SetLength(Dir, Pred(Length(Dir)));
  1692. // Optimization. Avoid iterating possibly thousands of nodes,
  1693. // when the node we are looking for is the selected node or its ancestor.
  1694. // This is often the case, when navigating under node that has lot of siblings.
  1695. // Typically, when navigating in user's profile folder, and there are many [thousands] other user profile folders.
  1696. if (SelectionHierarchyHeight > 0) and
  1697. // Change of selection might indicate that the tree was rebuilt meanwhile and
  1698. // the references in SelectionHierarchy might not be valid anymore
  1699. (Selected = SelectionHierarchy[SelectionHierarchyHeight - 1]) and
  1700. (Level < SelectionHierarchyHeight) and
  1701. (Uppercase(GetDirName(SelectionHierarchy[Level])) = Dir) then
  1702. begin
  1703. Result := SelectionHierarchy[Level];
  1704. end
  1705. else
  1706. begin
  1707. // Paths have diverted
  1708. SelectionHierarchyHeight := 0;
  1709. Node := ParentNode.GetFirstChild;
  1710. if (not Assigned(Node)) and (not ExistingOnly) then
  1711. begin
  1712. ValidateDirectoryEx(ParentNode, rsRecursiveExisting, True);
  1713. Node := ParentNode.GetFirstChild;
  1714. end;
  1715. Result := nil;
  1716. while (not Assigned(Result)) and Assigned(Node) do
  1717. begin
  1718. if UpperCase(GetDirName(Node)) = Dir then
  1719. begin
  1720. Result := Node;
  1721. end
  1722. else
  1723. begin
  1724. Node := ParentNode.GetNextChild(Node);
  1725. end;
  1726. end;
  1727. end;
  1728. if Assigned(Result) and (Length(Path) > 0) then
  1729. begin
  1730. Result := SearchSubDirs(Result, Path, Level + 1);
  1731. end;
  1732. end;
  1733. function SearchSubDirs(ParentNode: TTreeNode; Path: string; Level: Integer): TTreeNode;
  1734. var
  1735. ParentPath, SubPath: string;
  1736. SRec: TSearchRec;
  1737. ParentNodeData: TNodeData;
  1738. begin
  1739. Result := nil;
  1740. if Length(Path) > 0 then
  1741. begin
  1742. ParentNodeData := TNodeData(ParentNode.Data);
  1743. if (not ParentNodeData.Scanned) and (not ExistingOnly) then
  1744. begin
  1745. ReadSubDirs(ParentNode);
  1746. end;
  1747. // Factored out of DoSearchSubDirs is remnant of Bug 956 superceded by Bug 1320
  1748. Result := DoSearchSubDirs(ParentNode, Path, Level);
  1749. if (not Assigned(Result)) and (not ExistingOnly) then
  1750. begin
  1751. ParentPath := NodePath(ParentNode);
  1752. if DirectoryExists(ApiPath(IncludeTrailingBackslash(ParentPath) + Path)) then
  1753. begin
  1754. SubPath := IncludeTrailingBackslash(ParentPath) + ExcludeTrailingBackslash(ExtractFirstName(Path));
  1755. if FindFirstSubDir(SubPath, SRec) then
  1756. begin
  1757. AddChildNode(ParentNode, ParentPath, SRec);
  1758. if Assigned(ParentNodeData.DelayedExclude) then
  1759. ParentNodeData.DelayedExclude.Add(SRec.Name);
  1760. SortChildren(ParentNode, False);
  1761. FindClose(SRec);
  1762. end;
  1763. Result := DoSearchSubDirs(ParentNode, Path, Level);
  1764. end;
  1765. end;
  1766. end;
  1767. end; {SearchSubDirs}
  1768. var
  1769. Drive: string;
  1770. P, I: Integer;
  1771. RootNode, Node: TTreeNode;
  1772. begin {FindNodeToPath}
  1773. Result := nil;
  1774. if Length(Path) < 3 then
  1775. Exit;
  1776. // Particularly when used by TDirView to delegate browsing to
  1777. // hidden drive view, the handle may not be created
  1778. HandleNeeded;
  1779. Drive := DriveInfo.GetDriveKey(Path);
  1780. if (not Assigned(GetDriveStatus(Drive).RootNode)) and
  1781. // hidden or possibly recently un-hidden by other drive view (refresh is pending)
  1782. (DriveInfo.Get(Drive).Valid or DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy) then
  1783. begin
  1784. if DriveInfo.Get(Drive).ValidButHiddenByDrivePolicy then
  1785. DriveInfo.OverrideDrivePolicy(Drive);
  1786. if DriveInfo.Get(Drive).Valid then
  1787. begin
  1788. CancelDriveRefresh; // cancel a possible pending refresh (see the previous comment)
  1789. RefreshRootNodes(dsAll or dvdsRereadAllways); // overkill and is likely already called by GetDriveStatus
  1790. DoRefreshDrives(False);
  1791. end;
  1792. end;
  1793. if Assigned(GetDriveStatus(Drive).RootNode) then
  1794. begin
  1795. if DriveInfo.IsRealDrive(Drive) then
  1796. begin
  1797. System.Delete(Path, 1, 3);
  1798. end
  1799. else
  1800. if IsUncPath(Path) then
  1801. begin
  1802. System.Delete(Path, 1, 2);
  1803. P := Pos('\', Path);
  1804. if P = 0 then
  1805. begin
  1806. Path := '';
  1807. end
  1808. else
  1809. begin
  1810. System.Delete(Path, 1, P);
  1811. P := Pos('\', Path);
  1812. if P = 0 then
  1813. begin
  1814. Path := '';
  1815. end
  1816. else
  1817. begin
  1818. System.Delete(Path, 1, P);
  1819. end;
  1820. end;
  1821. end
  1822. else
  1823. begin
  1824. raise EConvertError.Create(Format(ErrorInvalidDrive, [Path]))
  1825. end;
  1826. if Length(Path) > 0 then
  1827. begin
  1828. if (not GetDriveStatus(Drive).Scanned) and (not ExistingOnly) then
  1829. begin
  1830. ScanDrive(Drive);
  1831. end;
  1832. Node := Selected;
  1833. RootNode := GetDriveStatus(Drive).RootNode;
  1834. if not Assigned(Node) then
  1835. begin
  1836. SelectionHierarchyHeight := 0;
  1837. end
  1838. else
  1839. begin
  1840. SelectionHierarchyHeight := Node.Level + 1;
  1841. SetLength(SelectionHierarchy, SelectionHierarchyHeight);
  1842. for I := SelectionHierarchyHeight - 1 downto 0 do
  1843. begin
  1844. SelectionHierarchy[I] := Node;
  1845. Node := Node.Parent;
  1846. end;
  1847. Assert(Selected = SelectionHierarchy[SelectionHierarchyHeight - 1]);
  1848. // Different drive - nothing to optimize
  1849. if RootNode <> SelectionHierarchy[0] then
  1850. SelectionHierarchyHeight := 0;
  1851. end;
  1852. Result := SearchSubDirs(RootNode, UpperCase(Path), 1);
  1853. end
  1854. else Result := GetDriveStatus(Drive).RootNode;
  1855. end;
  1856. end; {FindNodetoPath}
  1857. function TDriveView.FindNodeToPath(Path: string): TTreeNode;
  1858. begin
  1859. Result := DoFindNodeToPath(Path, False);
  1860. end;
  1861. function TDriveView.TryFindNodeToPath(Path: string): TTreeNode;
  1862. begin
  1863. Result := DoFindNodeToPath(Path, True);
  1864. end;
  1865. function TDriveView.GetSubDir(var SRec: TSearchRec): Boolean;
  1866. begin
  1867. Result := True;
  1868. while Result and
  1869. ((SRec.Name = '.' ) or
  1870. (SRec.Name = '..') or
  1871. ((SRec.Attr and faDirectory) = 0)) do
  1872. begin
  1873. if FindNext(SRec) <> 0 then
  1874. begin
  1875. Result := False;
  1876. end;
  1877. end;
  1878. end;
  1879. function TDriveView.FindFirstSubDir(Path: string; var SRec: TSearchRec): Boolean;
  1880. begin
  1881. Result := (FindFirstEx(ApiPath(Path), DirAttrMask, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS, FindExSearchLimitToDirectories) = 0);
  1882. if Result then
  1883. begin
  1884. Result := GetSubDir(SRec);
  1885. if not Result then FindClose(SRec);
  1886. end;
  1887. end;
  1888. function TDriveView.FindNextSubDir(var SRec: TSearchRec): Boolean;
  1889. begin
  1890. Result := (FindNext(SRec) = 0) and GetSubDir(SRec);
  1891. end;
  1892. function TDriveView.ReadSubDirsBatch(Node: TTreeNode; var SRec: TSearchRec; CheckInterval, Limit: Integer): Boolean;
  1893. var
  1894. Start: TDateTime;
  1895. Cont: Boolean;
  1896. Path: string;
  1897. Count: Integer;
  1898. DelayedExclude: TStringList;
  1899. begin
  1900. Start := Now;
  1901. Path := NodePath(Node);
  1902. Result := True;
  1903. Count := 0;
  1904. DelayedExclude := TNodeData(Node.Data).DelayedExclude;
  1905. // At least from SetDirectory > DoFindNodeToPath and CanExpand, this is not called within BeginUpdate/EndUpdate block.
  1906. // But in any case, adding it here makes expanding (which calls CanExpand) noticeably slower, when there are lot of nodes,
  1907. // because EndUpdate triggers TVN_GETDISPINFO for all nodes in the tree.
  1908. repeat
  1909. if (not Assigned(DelayedExclude)) or
  1910. (DelayedExclude.IndexOf(SRec.Name) < 0) then
  1911. begin
  1912. AddChildNode(Node, Path, SRec);
  1913. Inc(Count);
  1914. end;
  1915. Cont := FindNextSubDir(SRec);
  1916. // There are two other directory reading loops, where this is not checked
  1917. if Cont and
  1918. ((Count mod CheckInterval) = 0) and
  1919. (Limit > 0) and
  1920. (MilliSecondsBetween(Now, Start) > Limit) then
  1921. begin
  1922. Result := False;
  1923. Cont := False;
  1924. end
  1925. until not Cont;
  1926. if Result then
  1927. FindClose(Srec);
  1928. end;
  1929. procedure TDriveView.DelayedNodeTimer(Sender: TObject);
  1930. var
  1931. Node: TTreeNode;
  1932. NodeData: TNodeData;
  1933. begin
  1934. Assert(FDelayedNodes.Count > 0);
  1935. if FDelayedNodes.Count > 0 then
  1936. begin
  1937. // Control was recreated
  1938. if not Assigned(FDelayedNodes.Objects[0]) then
  1939. begin
  1940. FDelayedNodes.Objects[0] := TryFindNodeToPath(FDelayedNodes.Strings[0]);
  1941. end;
  1942. Node := TTreeNode(FDelayedNodes.Objects[0]);
  1943. if not Assigned(Node) then
  1944. begin
  1945. FDelayedNodes.Delete(0);
  1946. end
  1947. else
  1948. begin
  1949. NodeData := TNodeData(Node.Data);
  1950. if ReadSubDirsBatch(Node, NodeData.DelayedSrec, 10, 50) then
  1951. begin
  1952. FreeAndNil(NodeData.DelayedExclude);
  1953. FDelayedNodes.Delete(0);
  1954. SortChildren(Node, False);
  1955. end;
  1956. end;
  1957. end;
  1958. UpdateDelayedNodeTimer;
  1959. end;
  1960. procedure TDriveView.UpdateDelayedNodeTimer;
  1961. begin
  1962. FDelayedNodeTimer.Enabled := HandleAllocated and (FDelayedNodes.Count > 0);
  1963. end;
  1964. procedure TDriveView.ReadSubDirs(Node: TTreeNode);
  1965. var
  1966. SRec: TSearchRec;
  1967. NodeData: TNodeData;
  1968. Path: string;
  1969. CheckInterval, Limit: Integer;
  1970. begin
  1971. NodeData := TNodeData(Node.Data);
  1972. Path := NodePath(Node);
  1973. if not FindFirstSubDir(IncludeTrailingBackslash(Path) + '*.*', SRec) then
  1974. begin
  1975. Node.HasChildren := False;
  1976. end
  1977. else
  1978. begin
  1979. CheckInterval := 100;
  1980. Limit := DriveViewLoadingTooLongLimit * 1000;
  1981. if not Showing then
  1982. begin
  1983. Limit := Limit div 10;
  1984. CheckInterval := CheckInterval div 10;
  1985. end;
  1986. if not ReadSubDirsBatch(Node, SRec, CheckInterval, Limit) then
  1987. begin
  1988. NodeData.DelayedSrec := SRec;
  1989. NodeData.DelayedExclude := TStringList.Create;
  1990. NodeData.DelayedExclude.CaseSensitive := False;
  1991. NodeData.DelayedExclude.Sorted := True;
  1992. FDelayedNodes.AddObject(Path, Node);
  1993. Assert(FDelayedNodes.Count < 20); // if more, something went likely wrong
  1994. UpdateDelayedNodeTimer;
  1995. end;
  1996. SortChildren(Node, False);
  1997. end;
  1998. NodeData.Scanned := True;
  1999. Application.ProcessMessages;
  2000. end; {ReadSubDirs}
  2001. procedure TDriveView.CancelDelayedNode(Node: TTreeNode);
  2002. var
  2003. NodeData: TNodeData;
  2004. begin
  2005. NodeData := TNodeData(Node.Data);
  2006. FindClose(NodeData.DelayedSrec);
  2007. FreeAndNil(NodeData.DelayedExclude);
  2008. end;
  2009. procedure TDriveView.DeleteNode(Node: TTreeNode);
  2010. var
  2011. ValidNode: TTreeNode;
  2012. begin
  2013. if Assigned(Selected) and Assigned(Node.Parent) and
  2014. ((Selected = Node) or Selected.HasAsParent(Node)) then
  2015. begin
  2016. ValidNode := Node.Parent;
  2017. while (not NodePathExists(ValidNode)) and Assigned(ValidNode.Parent) do
  2018. ValidNode := ValidNode.Parent;
  2019. Selected := ValidNode;
  2020. end;
  2021. if DropTarget = Node then
  2022. DropTarget := nil;
  2023. Node.Delete;
  2024. end;
  2025. function TDriveView.CallBackValidateDir(var Node: TTreeNode; Data: Pointer): Boolean;
  2026. var
  2027. WorkNode: TTreeNode;
  2028. DelNode: TTreeNode;
  2029. SRec: TSearchRec;
  2030. SrecList: TStringList;
  2031. SubDirList: TStringList;
  2032. R: Boolean;
  2033. Index: Integer;
  2034. NewDirFound: Boolean;
  2035. ParentDir: string;
  2036. NodeData: TNodeData;
  2037. ScanDirInfo: PScanDirInfo;
  2038. begin {CallBackValidateDir}
  2039. Result := True;
  2040. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  2041. Exit;
  2042. NewDirFound := False;
  2043. ScanDirInfo := PScanDirInfo(Data);
  2044. {Check, if directory still exists: (but not with root directory) }
  2045. if Assigned(Node.Parent) and (ScanDirInfo^.StartNode = Node) and
  2046. (not NodePathExists(Node)) then
  2047. begin
  2048. DeleteNode(Node);
  2049. Node := nil;
  2050. Exit;
  2051. end;
  2052. WorkNode := Node.GetFirstChild;
  2053. NodeData := TNodeData(Node.Data);
  2054. if NodeData.Scanned and Assigned(WorkNode) then
  2055. {if node was already scanned: check wether the existing subnodes are still alive
  2056. and add all new subdirectories as subnodes:}
  2057. begin
  2058. if DoScanDir(Node) then
  2059. begin
  2060. ParentDir := IncludeTrailingBackslash(NodePath(Node));
  2061. {Build list of existing subnodes:}
  2062. SubDirList := TStringList.Create;
  2063. SubDirList.CaseSensitive := True; // We want to reflect changes in subfolder name case
  2064. while Assigned(WorkNode) do
  2065. begin
  2066. SubDirList.Add(TNodeData(WorkNode.Data).DirName);
  2067. WorkNode := Node.GetNextChild(WorkNode);
  2068. end;
  2069. // Nodes are sorted using natural sorting, while TStringList.Find uses simple sorting
  2070. SubDirList.Sort;
  2071. SRecList := TStringList.Create;
  2072. SRecList.CaseSensitive := True;
  2073. R := FindFirstSubDir(ParentDir + '*.*', SRec);
  2074. while R do
  2075. begin
  2076. SrecList.Add(Srec.Name);
  2077. if not SubDirList.Find(Srec.Name, Index) then
  2078. {Subnode does not exists: add it:}
  2079. begin
  2080. AddChildNode(Node, ParentDir, SRec);
  2081. NewDirFound := True;
  2082. end;
  2083. R := FindNextSubDir(Srec);
  2084. end;
  2085. FindClose(Srec);
  2086. Sreclist.Sort;
  2087. {Remove not existing subnodes:}
  2088. WorkNode := Node.GetFirstChild;
  2089. while Assigned(WorkNode) do
  2090. begin
  2091. if not Assigned(WorkNode.Data) or
  2092. not SrecList.Find(TNodeData(WorkNode.Data).DirName, Index) then
  2093. begin
  2094. DelNode := WorkNode;
  2095. WorkNode := Node.GetNextChild(WorkNode);
  2096. DeleteNode(DelNode);
  2097. end
  2098. else
  2099. begin
  2100. if (SrecList[Index] <> TNodeData(WorkNode.Data).DirName) then
  2101. begin
  2102. {Case of directory letters has changed:}
  2103. TNodeData(WorkNode.Data).DirName := SrecList[Index];
  2104. WorkNode.Text := SrecList[Index];
  2105. end;
  2106. WorkNode := Node.GetNextChild(WorkNode);
  2107. end;
  2108. end;
  2109. SrecList.Free;
  2110. SubDirList.Free;
  2111. {Sort subnodes:}
  2112. if NewDirFound then
  2113. SortChildren(Node, False);
  2114. end;
  2115. end
  2116. else
  2117. {Node was not already scanned:}
  2118. if (ScanDirInfo^.SearchNewDirs or
  2119. NodeData.Scanned or
  2120. (Node = ScanDirInfo^.StartNode)) and
  2121. DoScanDir(Node) then
  2122. begin
  2123. ReadSubDirs(Node);
  2124. end;
  2125. end; {CallBackValidateDir}
  2126. procedure TDriveView.RebuildTree;
  2127. var
  2128. Drive: string;
  2129. begin
  2130. for Drive in FDriveStatus.Keys do
  2131. with GetDriveStatus(Drive) do
  2132. if Assigned(RootNode) and Scanned then
  2133. ValidateDirectory(RootNode);
  2134. end;
  2135. procedure TDriveView.ValidateCurrentDirectoryIfNotMonitoring;
  2136. begin
  2137. if Assigned(Selected) and
  2138. not Assigned(GetDriveStatus(GetDriveToNode(Selected)).DiscMonitor) then
  2139. begin
  2140. ValidateDirectory(Selected);
  2141. end;
  2142. end;
  2143. procedure TDriveView.ValidateDirectoryEx(Node: TTreeNode; Recurse: TRecursiveScan;
  2144. NewDirs: Boolean);
  2145. var
  2146. Info: PScanDirInfo;
  2147. SelDir: string;
  2148. SaveCursor: TCursor;
  2149. RestartWatchThread: Boolean;
  2150. SaveCanChange: Boolean;
  2151. CurrentPath: string;
  2152. Drive: string;
  2153. begin
  2154. if Assigned(Node) and Assigned(Node.Data) and
  2155. (not FValidateFlag) and DoScanDir(Node) then
  2156. begin
  2157. SelDir := Directory;
  2158. SaveCursor := Screen.Cursor;
  2159. if Self.Focused and (Screen.Cursor <> crHourGlass) then
  2160. Screen.Cursor := crHourGlass;
  2161. CurrentPath := NodePath(Node);
  2162. Drive := DriveInfo.GetDriveKey(CurrentPath);
  2163. if not Assigned(Node.Parent) then
  2164. GetDriveStatus(Drive).ChangeTimer.Enabled := False;
  2165. RestartWatchThread := WatchThreadActive;
  2166. try
  2167. if WatchThreadActive then
  2168. StopWatchThread;
  2169. FValidateFlag := True;
  2170. FSysColorChangePending := False;
  2171. New(Info);
  2172. Info^.StartNode := Node;
  2173. Info^.SearchNewDirs := NewDirs;
  2174. Info^.DriveType := DriveInfo.Get(Drive).DriveType;
  2175. SaveCanChange := FCanChange;
  2176. FCanChange := True;
  2177. FChangeFlag := False;
  2178. Items.BeginUpdate;
  2179. try
  2180. IterateSubTree(Node, CallBackValidateDir, Recurse, coScanStartNode, Info);
  2181. finally
  2182. Items.EndUpdate;
  2183. end;
  2184. FValidateFlag := False;
  2185. if (not Assigned(Selected)) and (Length(SelDir) > 0) then
  2186. Directory := ExtractFileDrive(SelDir);
  2187. if (SelDir <> Directory) and (not FChangeFlag) then
  2188. Change(Selected);
  2189. FCanChange := SaveCanChange;
  2190. Dispose(Info);
  2191. finally
  2192. if RestartWatchThread and FWatchDirectory and not WatchThreadActive then
  2193. StartWatchThread;
  2194. if Screen.Cursor <> SaveCursor then
  2195. Screen.Cursor := SaveCursor;
  2196. if FSysColorChangePending then
  2197. begin
  2198. FSysColorChangePending := False;
  2199. if HandleAllocated then Perform(CM_SYSCOLORCHANGE, 0, 0);
  2200. end;
  2201. end;
  2202. end;
  2203. end; {ValidateDirectoryEx}
  2204. function TDriveView.GetDriveTypeToNode(Node: TTreeNode): Integer;
  2205. begin
  2206. Assert(Assigned(Node));
  2207. Result := DriveInfo.Get(GetDriveToNode(Node)).DriveType;
  2208. end; {GetDriveTypeToNode}
  2209. procedure TDriveView.CreateWatchThread(Drive: string);
  2210. begin
  2211. if csDesigning in ComponentState then
  2212. Exit;
  2213. if (not Assigned(GetDriveStatus(Drive).DiscMonitor)) and
  2214. FWatchDirectory and
  2215. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) then
  2216. begin
  2217. with GetDriveStatus(Drive) do
  2218. begin
  2219. DiscMonitor := TDiscMonitor.Create(Self);
  2220. DiscMonitor.ChangeDelay := msThreadChangeDelay;
  2221. DiscMonitor.SubTree := True;
  2222. DiscMonitor.Filters := [moDirName];
  2223. DiscMonitor.OnChange := ChangeDetected;
  2224. DiscMonitor.OnInvalid := ChangeInvalid;
  2225. DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
  2226. DiscMonitor.Open;
  2227. end;
  2228. UpdateDriveNotifications(Drive);
  2229. end;
  2230. end; {CreateWatchThread}
  2231. procedure TDriveView.SetWatchDirectory(Value: Boolean);
  2232. begin
  2233. if FWatchDirectory <> Value then
  2234. begin
  2235. FWatchDirectory := Value;
  2236. if (not (csDesigning in ComponentState)) and Value then
  2237. StartAllWatchThreads
  2238. else
  2239. StopAllWatchThreads;
  2240. end;
  2241. end; {SetAutoScan}
  2242. procedure TDriveView.SetDirView(Value: TDirView);
  2243. begin
  2244. if Assigned(FDirView) then
  2245. FDirView.DriveView := nil;
  2246. FDirView := Value;
  2247. if Assigned(FDirView) then
  2248. FDirView.DriveView := Self;
  2249. end; {SetDirView}
  2250. function TDriveView.NodeWatched(Node: TTreeNode): Boolean;
  2251. var
  2252. Drive: string;
  2253. begin
  2254. Drive := GetDriveToNode(Node);
  2255. Result := WatchThreadActive(Drive);
  2256. end; {NodeWatched}
  2257. procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
  2258. const ErrorStr: string);
  2259. var
  2260. Drive: string;
  2261. begin
  2262. Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
  2263. with GetDriveStatus(Drive) do
  2264. begin
  2265. DiscMonitor.Close;
  2266. end;
  2267. UpdateDriveNotifications(Drive);
  2268. end; {DirWatchChangeInvalid}
  2269. procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
  2270. var SubdirsChanged: Boolean);
  2271. var
  2272. DirChanged: string;
  2273. begin
  2274. if Sender is TDiscMonitor then
  2275. begin
  2276. DirChanged := (Sender as TDiscMonitor).Directories[0];
  2277. if Length(DirChanged) > 0 then
  2278. begin
  2279. with GetDriveStatus(DriveInfo.GetDriveKey(DirChanged)) do
  2280. begin
  2281. ChangeTimer.Interval := 0;
  2282. ChangeTimer.Interval := FChangeInterval;
  2283. ChangeTimer.Enabled := True;
  2284. end;
  2285. end;
  2286. end;
  2287. end; {DirWatchChangeDetected}
  2288. procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
  2289. var
  2290. DriveStatusPair: TDriveStatusPair;
  2291. begin
  2292. if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
  2293. begin
  2294. for DriveStatusPair in FDriveStatus do
  2295. begin
  2296. if DriveStatusPair.Value.ChangeTimer = Sender then
  2297. begin
  2298. // Messages are processed during ValidateDirectory, so we may detect another change while
  2299. // updating the directory. Prevent the recursion.
  2300. // But retry the update afterwards (by reenabling the timer in ChangeDetected)
  2301. SuspendChangeTimer;
  2302. try
  2303. with DriveStatusPair.Value.ChangeTimer do
  2304. begin
  2305. Interval := 0;
  2306. Enabled := False;
  2307. end;
  2308. if Assigned(DriveStatusPair.Value.RootNode) then
  2309. begin
  2310. {Check also collapsed (invisible) subdirectories:}
  2311. ValidateDirectory(DriveStatusPair.Value.RootNode);
  2312. end;
  2313. finally
  2314. ResumeChangeTimer;
  2315. end;
  2316. end;
  2317. end;
  2318. end;
  2319. end; {ChangeTimerOnTimer}
  2320. procedure TDriveView.UpdateDriveNotifications(Drive: string);
  2321. var
  2322. NeedNotifications: Boolean;
  2323. Path: string;
  2324. DevBroadcastHandle: DEV_BROADCAST_HANDLE;
  2325. Size: Integer;
  2326. begin
  2327. if DriveInfo.IsFixedDrive(Drive) then
  2328. begin
  2329. with GetDriveStatus(Drive) do
  2330. begin
  2331. NeedNotifications :=
  2332. WatchThreadActive(Drive) and
  2333. (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) and
  2334. DriveInfo.Get(Drive).DriveReady;
  2335. if NeedNotifications <> (DriveHandle <> INVALID_HANDLE_VALUE) then
  2336. begin
  2337. if NeedNotifications then
  2338. begin
  2339. Path := DriveInfo.GetDriveRoot(Drive);
  2340. DriveHandle :=
  2341. CreateFile(PChar(Path), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
  2342. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0);
  2343. if DriveHandle <> INVALID_HANDLE_VALUE then
  2344. begin
  2345. Size := SizeOf(DevBroadcastHandle);
  2346. ZeroMemory(@DevBroadcastHandle, Size);
  2347. DevBroadcastHandle.dbch_size := Size;
  2348. DevBroadcastHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
  2349. DevBroadcastHandle.dbch_handle := DriveHandle;
  2350. NotificationHandle :=
  2351. RegisterDeviceNotification(FInternalWindowHandle, @DevBroadcastHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  2352. if NotificationHandle = nil then
  2353. begin
  2354. CloseHandle(DriveHandle);
  2355. DriveHandle := INVALID_HANDLE_VALUE;
  2356. end;
  2357. end;
  2358. end
  2359. else
  2360. begin
  2361. UnregisterDeviceNotification(NotificationHandle);
  2362. NotificationHandle := nil;
  2363. CloseHandle(DriveHandle);
  2364. DriveHandle := INVALID_HANDLE_VALUE;
  2365. end;
  2366. end;
  2367. end;
  2368. end;
  2369. end;
  2370. procedure TDriveView.StartWatchThread;
  2371. var
  2372. Drive: string;
  2373. begin
  2374. if (csDesigning in ComponentState) or
  2375. not Assigned(Selected) or
  2376. not fWatchDirectory then Exit;
  2377. Drive := GetDriveToNode(Selected);
  2378. with GetDriveStatus(Drive) do
  2379. begin
  2380. if not Assigned(DiscMonitor) then
  2381. CreateWatchThread(Drive);
  2382. if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
  2383. DiscMonitor.Enabled := True;
  2384. end;
  2385. UpdateDriveNotifications(Drive);
  2386. end; {StartWatchThread}
  2387. procedure TDriveView.StopWatchThread;
  2388. var
  2389. Drive: string;
  2390. begin
  2391. if Assigned(Selected) then
  2392. begin
  2393. Drive := GetDriveToNode(Selected);
  2394. with GetDriveStatus(Drive) do
  2395. if Assigned(DiscMonitor) then
  2396. DiscMonitor.Enabled := False;
  2397. UpdateDriveNotifications(Drive);
  2398. end;
  2399. end; {StopWatchThread}
  2400. procedure TDriveView.SuspendChangeTimer;
  2401. begin
  2402. Inc(FChangeTimerSuspended);
  2403. end;
  2404. procedure TDriveView.ResumeChangeTimer;
  2405. begin
  2406. Assert(FChangeTimerSuspended > 0);
  2407. Dec(FChangeTimerSuspended);
  2408. end;
  2409. procedure TDriveView.TerminateWatchThread(Drive: string);
  2410. begin
  2411. with GetDriveStatus(Drive) do
  2412. if Assigned(DiscMonitor) then
  2413. begin
  2414. DiscMonitor.Free;
  2415. DiscMonitor := nil;
  2416. end;
  2417. UpdateDriveNotifications(Drive);
  2418. end; {StopWatchThread}
  2419. procedure TDriveView.StartAllWatchThreads;
  2420. var
  2421. DriveStatusPair: TDriveStatusPair;
  2422. Drive: string;
  2423. begin
  2424. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2425. Exit;
  2426. for DriveStatusPair in FDriveStatus do
  2427. with DriveStatusPair.Value do
  2428. if Scanned then
  2429. begin
  2430. if not Assigned(DiscMonitor) then
  2431. CreateWatchThread(DriveStatusPair.Key);
  2432. if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
  2433. begin
  2434. DiscMonitor.Open;
  2435. UpdateDriveNotifications(DriveStatusPair.Key);
  2436. end;
  2437. end;
  2438. if Assigned(Selected) then
  2439. begin
  2440. Drive := GetDriveToNode(Selected);
  2441. if not DriveInfo.IsFixedDrive(Drive) then
  2442. begin
  2443. StartWatchThread;
  2444. end;
  2445. end;
  2446. end; {StartAllWatchThreads}
  2447. procedure TDriveView.StopAllWatchThreads;
  2448. var
  2449. DriveStatusPair: TDriveStatusPair;
  2450. begin
  2451. if (csDesigning in ComponentState) or (not FWatchDirectory) then
  2452. Exit;
  2453. for DriveStatusPair in FDriveStatus do
  2454. with DriveStatusPair.Value do
  2455. begin
  2456. if Assigned(DiscMonitor) then
  2457. begin
  2458. DiscMonitor.Close;
  2459. UpdateDriveNotifications(DriveStatusPair.Key);
  2460. end;
  2461. end;
  2462. end; {StopAllWatchThreads}
  2463. function TDriveView.WatchThreadActive(Drive: string): Boolean;
  2464. begin
  2465. Result := FWatchDirectory and
  2466. Assigned(GetDriveStatus(Drive).DiscMonitor) and
  2467. GetDriveStatus(Drive).DiscMonitor.Active and
  2468. GetDriveStatus(Drive).DiscMonitor.Enabled;
  2469. end; {WatchThreadActive}
  2470. function TDriveView.WatchThreadActive: Boolean;
  2471. var
  2472. Drive: string;
  2473. begin
  2474. if not Assigned(Selected) then
  2475. begin
  2476. Result := False;
  2477. Exit;
  2478. end;
  2479. Drive := GetDriveToNode(Selected);
  2480. Result := WatchThreadActive(Drive);
  2481. end; {WatchThreadActive}
  2482. function TDriveView.FindPathNode(Path: string): TTreeNode;
  2483. var
  2484. PossiblyHiddenPath: string;
  2485. Attrs: Integer;
  2486. begin
  2487. if Assigned(FOnNeedHiddenDirectories) and
  2488. (not ShowHiddenDirs) and
  2489. DirectoryExistsFix(Path) then // do not even bother if the path does not exist
  2490. begin
  2491. PossiblyHiddenPath := ExcludeTrailingPathDelimiter(Path);
  2492. while (PossiblyHiddenPath <> '') and
  2493. (not IsRootPath(PossiblyHiddenPath)) do // Drives have hidden attribute
  2494. begin
  2495. Attrs := FileGetAttr(PossiblyHiddenPath, False);
  2496. if (Attrs and faHidden) = faHidden then
  2497. begin
  2498. if Assigned(FOnNeedHiddenDirectories) then
  2499. begin
  2500. FOnNeedHiddenDirectories(Self);
  2501. end;
  2502. Break;
  2503. end
  2504. else
  2505. begin
  2506. PossiblyHiddenPath := ExtractFileDir(PossiblyHiddenPath);
  2507. end;
  2508. end;
  2509. end;
  2510. {Find existing path or parent path of not existing path:}
  2511. repeat
  2512. Result := FindNodeToPath(Path);
  2513. if not Assigned(Result) then
  2514. Path := ExtractFilePath(ExcludeTrailingBackslash(Path));
  2515. until Assigned(Result) or (Length(Path) < 3);
  2516. end;
  2517. procedure TDriveView.SetDirectory(Value: string);
  2518. begin
  2519. Value := IncludeTrailingBackslash(Value);
  2520. FDirectory := Value;
  2521. inherited;
  2522. if Assigned(Selected) and (not Assigned(Selected.Parent)) then
  2523. begin
  2524. if not GetDriveStatus(GetDriveToNode(Selected)).Scanned then
  2525. ScanDrive(GetDriveToNode(Selected));
  2526. end;
  2527. end; {SetDirectory}
  2528. function TDriveView.GetDirName(Node: TTreeNode): string;
  2529. begin
  2530. if Assigned(Node) and Assigned(Node.Data) then
  2531. Result := TNodeData(Node.Data).DirName
  2532. else
  2533. Result := '';
  2534. end; {GetDirName}
  2535. {GetDrive: returns the drive of the Node.}
  2536. function TDriveView.GetDriveToNode(Node: TTreeNode): string;
  2537. var
  2538. Path: string;
  2539. begin
  2540. if (not Assigned (Node)) or (not Assigned(Node.Data)) then
  2541. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDrive']));
  2542. Path := NodePath(Node);
  2543. Result := DriveInfo.GetDriveKey(Path);
  2544. end; {GetDrive}
  2545. {RootNode: returns the rootnode to the Node:}
  2546. function TDriveView.RootNode(Node: TTreeNode): TTreeNode;
  2547. begin
  2548. Result := Node;
  2549. if not Assigned(Node) then
  2550. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['RootNode']));
  2551. while Assigned(Result.Parent) do
  2552. Result := Result.Parent;
  2553. end; {RootNode}
  2554. function TDriveView.GetDisplayName(Node: TTreeNode): string;
  2555. begin
  2556. Result := '';
  2557. if (not Assigned(Node)) or (not Assigned(Node.Data)) then
  2558. raise ENodeNotAssigned.Create(Format(ErrorNodeNA, ['GetDisplayName']));
  2559. if not Assigned(Node.Parent) then Result := GetDriveText(GetDriveToNode(Node))
  2560. else
  2561. begin
  2562. Result := GetDirName(Node);
  2563. end;
  2564. end; {GetDisplayName}
  2565. procedure TDriveView.SetShowVolLabel(ShowIt: Boolean);
  2566. begin
  2567. if ShowIt = FShowVolLabel then
  2568. Exit;
  2569. FShowVolLabel := ShowIt;
  2570. RefreshRootNodes(dvdsFloppy);
  2571. end; {SetShowVolLabel}
  2572. procedure TDriveView.DisplayContextMenu(Node: TTreeNode; Point: TPoint);
  2573. var
  2574. Verb: string;
  2575. DirWatched: Boolean;
  2576. begin
  2577. DirWatched := NodeWatched(Node) and WatchThreadActive;
  2578. Assert(Node <> nil);
  2579. if Node <> Selected then
  2580. DropTarget := Node;
  2581. Verb := EmptyStr;
  2582. if Assigned(FOnDisplayContextMenu) then
  2583. FOnDisplayContextMenu(Self);
  2584. ShellDisplayContextMenu(FParentForm.Handle, Point, NodePathName(Node),
  2585. CanEdit(Node), Verb, False);
  2586. if Verb = shcRename then Node.EditText
  2587. else
  2588. if Verb = shcCut then
  2589. begin
  2590. LastClipBoardOperation := cboCut;
  2591. LastPathCut := NodePathName(Node);
  2592. end
  2593. else
  2594. if Verb = shcCopy then LastClipBoardOperation := cboCopy
  2595. else
  2596. if Verb = shcPaste then
  2597. PasteFromClipBoard(Node);
  2598. DropTarget := nil;
  2599. if not DirWatched then
  2600. ValidateDirectory(Node);
  2601. end; {DisplayContextMenu (2)}
  2602. procedure TDriveView.DisplayPropertiesMenu(Node: TTreeNode);
  2603. begin
  2604. Assert(Assigned(Node));
  2605. ShellExecuteContextCommand(FParentForm.Handle, shcProperties, NodePathName(Node));
  2606. end; {ContextMenu}
  2607. procedure TDriveView.SetSelected(Node: TTreeNode);
  2608. begin
  2609. if Node <> Selected then
  2610. begin
  2611. FChangeFlag := False;
  2612. FCanChange := True;
  2613. inherited Selected := Node;
  2614. if not FChangeFlag then
  2615. Change(Selected);
  2616. end;
  2617. end; {SetSelected}
  2618. {Called by TFileDeleteThread, when a file deletion was detected by the D&D receiving application:}
  2619. procedure TDriveView.SignalDirDelete(Sender: TObject; Files: TStringList);
  2620. begin
  2621. if Files.Count > 0 then
  2622. ValidateDirectory(FindNodeToPath(Files[0]));
  2623. end; {SignalDirDelete}
  2624. function TDriveView.DDSourceEffects: TDropEffectSet;
  2625. begin
  2626. if not Assigned(FDragNode.Parent) then
  2627. Result := [deLink]
  2628. else
  2629. Result := [deLink, deCopy, deMove];
  2630. end;
  2631. procedure TDriveView.DDChooseEffect(KeyState: Integer; var Effect: Integer; PreferredEffect: Integer);
  2632. begin
  2633. if DropTarget = nil then Effect := DROPEFFECT_NONE
  2634. else
  2635. if (KeyState and (MK_CONTROL or MK_SHIFT) = 0) and (PreferredEffect = 0) then
  2636. begin
  2637. if FDragDrive <> '' then
  2638. begin
  2639. if FExeDrag and DriveInfo.IsFixedDrive(GetDriveToNode(DropTarget)) and DriveInfo.IsFixedDrive(FDragDrive) then
  2640. begin
  2641. Effect := DROPEFFECT_LINK;
  2642. end
  2643. else
  2644. if (Effect = DROPEFFECT_COPY) and
  2645. (SameText(FDragDrive, GetDriveToNode(DropTarget)) and
  2646. (FDragDropFilesEx.AvailableDropEffects and DROPEFFECT_MOVE <> 0)) then
  2647. begin
  2648. Effect := DROPEFFECT_MOVE;
  2649. end;
  2650. end;
  2651. end;
  2652. inherited;
  2653. end;
  2654. function TDriveView.DragCompleteFileList: Boolean;
  2655. begin
  2656. Result := (GetDriveTypeToNode(FDragNode) <> DRIVE_REMOVABLE);
  2657. end;
  2658. function TDriveView.DDExecute: TDragResult;
  2659. var
  2660. WatchThreadOK: Boolean;
  2661. DragParentPath: string;
  2662. DragPath: string;
  2663. begin
  2664. WatchThreadOK := WatchThreadActive;
  2665. Result := FDragDropFilesEx.Execute(nil);
  2666. if (Result = drMove) and (not WatchThreadOK) then
  2667. begin
  2668. DragPath := NodePathName(FDragNode);
  2669. if Assigned(FDragNode.Parent) then
  2670. DragParentPath := NodePathName(FDragNode.Parent)
  2671. else
  2672. DragParentPath := DragPath;
  2673. if Assigned(FDragNode.Parent) or (DragParentPath <> NodePathName(Selected.Parent)) then
  2674. begin
  2675. FDragNode := FindNodeToPath(DragPath);
  2676. if Assigned(FDragNode) then
  2677. begin
  2678. FDragFileList.Clear;
  2679. FDragFileList.Add(DragPath);
  2680. TFileDeleteThread.Create(FDragFileList, MaxWaitTimeOut, SignalDirDelete);
  2681. end;
  2682. end;
  2683. end;
  2684. end;
  2685. procedure TDriveView.PerformDragDropFileOperation(Node: TTreeNode; Effect: Integer);
  2686. var
  2687. Index: Integer;
  2688. SourcePath: string;
  2689. SourceParentPath: string;
  2690. SourceIsDirectory: Boolean;
  2691. SaveCursor: TCursor;
  2692. SourceNode, TargetNode: TTreeNode;
  2693. TargetPath: string;
  2694. IsRecycleBin: Boolean;
  2695. begin
  2696. TargetPath := NodePathName(Node);
  2697. IsRecycleBin := NodeIsRecycleBin(Node);
  2698. if FDragDropFilesEx.FileList.Count = 0 then
  2699. Exit;
  2700. SaveCursor := Screen.Cursor;
  2701. Screen.Cursor := crHourGlass;
  2702. SourcePath := EmptyStr;
  2703. try
  2704. if (Effect = DROPEFFECT_COPY) or (Effect = DROPEFFECT_MOVE) then
  2705. begin
  2706. StopAllWatchThreads;
  2707. if Assigned(FDirView) then
  2708. FDirView.StopWatchThread;
  2709. if Assigned(DropSourceControl) and
  2710. (DropSourceControl is TDirView) and
  2711. (DropSourceControl <> FDirView) then
  2712. begin
  2713. TDirView(DropSourceControl).StopWatchThread;
  2714. end;
  2715. if DropFiles(
  2716. DragDropFilesEx, Effect, FFileOperator, TargetPath, false, IsRecycleBin, ConfirmDelete, ConfirmOverwrite, False,
  2717. Self, OnDDFileOperation, SourcePath, SourceIsDirectory) then
  2718. begin
  2719. if Assigned(FOnDDFileOperationExecuted) then
  2720. FOnDDFileOperationExecuted(Self, Effect, SourcePath, TargetPath);
  2721. end;
  2722. ClearDragFileList(FDragDropFilesEx.FileList);
  2723. // TDirView.PerformDragDropFileOperation validates the SourcePath and that actually seems correct
  2724. SourceParentPath := ExtractFilePath(ExcludeTrailingBackslash(SourcePath));
  2725. end
  2726. else
  2727. if Effect = DROPEFFECT_LINK then
  2728. { Create Link requested: }
  2729. begin
  2730. for Index := 0 to FDragDropFilesEx.FileList.Count - 1 do
  2731. begin
  2732. if not DropLink(PFDDListItem(FDragDropFilesEx.FileList[Index]), TargetPath) then
  2733. begin
  2734. DDError(DDCreateShortCutError);
  2735. end;
  2736. end;
  2737. end;
  2738. if Effect = DROPEFFECT_MOVE then
  2739. Items.BeginUpdate;
  2740. {Update source directory, if move-operation was performed:}
  2741. if ((Effect = DROPEFFECT_MOVE) or IsRecycleBin) then
  2742. begin
  2743. // See comment in corresponding operation in TDirView.PerformDragDropFileOperation
  2744. SourceNode := TryFindNodeToPath(SourceParentPath);
  2745. if Assigned(SourceNode) then
  2746. ValidateDirectory(SourceNode);
  2747. end;
  2748. {Update subdirectories of target directory:}
  2749. TargetNode := FindNodeToPath(TargetPath);
  2750. if Assigned(TargetNode) then
  2751. ValidateDirectory(TargetNode)
  2752. else
  2753. ValidateDirectory(GetDriveStatus(DriveInfo.GetDriveKey(TargetPath)).RootNode);
  2754. if Effect = DROPEFFECT_MOVE then
  2755. Items.EndUpdate;
  2756. {Update linked component TDirView:}
  2757. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2758. begin
  2759. case Effect of
  2760. DROPEFFECT_COPY,
  2761. DROPEFFECT_LINK:
  2762. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) then
  2763. FDirView.Reload2;
  2764. DROPEFFECT_MOVE:
  2765. if (IncludeTrailingBackslash(TargetPath) = IncludeTrailingBackslash(DirView.Path)) or
  2766. (IncludeTrailingBackslash(SourceParentPath) = IncludeTrailingBackslash(DirView.Path)) then
  2767. begin
  2768. if FDirView <> DropSourceControl then FDirView.Reload2;
  2769. end;
  2770. end; {Case}
  2771. end;
  2772. {Update the DropSource control, if files are moved and it is a TDirView:}
  2773. if (Effect = DROPEFFECT_MOVE) and (DropSourceControl is TDirView) then
  2774. begin
  2775. TDirView(DropSourceControl).ValidateSelectedFiles;
  2776. end;
  2777. finally
  2778. FFileOperator.OperandFrom.Clear;
  2779. FFileOperator.OperandTo.Clear;
  2780. StartAllWatchThreads;
  2781. if Assigned(FDirView) and (not FDirView.WatchThreadActive) then
  2782. FDirView.StartWatchThread;
  2783. if Assigned(DropSourceControl) and (DropSourceControl is TDirView) and
  2784. (not TDirView(DropSourceControl).WatchThreadActive) then
  2785. TDirView(DropSourceControl).StartWatchThread;
  2786. Screen.Cursor := SaveCursor;
  2787. end;
  2788. end; {PerformDragDropFileOperation}
  2789. function TDriveView.GetCanUndoCopyMove: Boolean;
  2790. begin
  2791. Result := Assigned(FFileOperator) and FFileOperator.CanUndo;
  2792. end; {CanUndoCopyMove}
  2793. function TDriveView.UndoCopyMove: Boolean;
  2794. var
  2795. LastTarget: string;
  2796. LastSource: string;
  2797. begin
  2798. Result := False;
  2799. if FFileOperator.CanUndo then
  2800. begin
  2801. Lasttarget := FFileOperator.LastOperandTo[0];
  2802. LastSource := FFileOperator.LastOperandFrom[0];
  2803. StopAllWatchThreads;
  2804. Result := FFileOperator.UndoExecute;
  2805. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastTarget)));
  2806. ValidateDirectory(FindNodeToPath(ExtractFilePath(LastSource)));
  2807. StartAllWatchThreads;
  2808. if Assigned(FDirView) then
  2809. with FDirView do
  2810. if not WatchThreadActive then
  2811. begin
  2812. if (IncludeTrailingBackslash(ExtractFilePath(LastTarget)) = IncludeTrailingBackslash(Path)) or
  2813. (IncludeTrailingBackslash(ExtractFilePath(LastSource)) = IncludeTrailingBackslash(Path)) then
  2814. Reload2;
  2815. end;
  2816. end;
  2817. end; {UndoCopyMove}
  2818. {Clipboard operations:}
  2819. procedure TDriveView.SetLastPathCut(Path: string);
  2820. var
  2821. Node: TTreeNode;
  2822. begin
  2823. if FLastPathCut <> Path then
  2824. begin
  2825. Node := FindNodeToPath(FLastPathCut);
  2826. if Assigned(Node) then
  2827. begin
  2828. FLastPathCut := Path;
  2829. Node.Cut := False;
  2830. end;
  2831. Node := FindNodeToPath(Path);
  2832. if Assigned(Node) then
  2833. begin
  2834. FLastPathCut := Path;
  2835. Node.Cut := True;
  2836. end;
  2837. end;
  2838. end; {SetLastNodeCut}
  2839. procedure TDriveView.EmptyClipboard;
  2840. begin
  2841. if Windows.OpenClipBoard(0) then
  2842. begin
  2843. Windows.EmptyClipBoard;
  2844. Windows.CloseClipBoard;
  2845. LastPathCut := '';
  2846. LastClipBoardOperation := cboNone;
  2847. if Assigned(FDirView) then
  2848. FDirView.EmptyClipboard;
  2849. end;
  2850. end; {EmptyClipBoard}
  2851. function TDriveView.CopyToClipBoard(Node: TTreeNode): Boolean;
  2852. begin
  2853. Result := Assigned(Selected);
  2854. if Result then
  2855. begin
  2856. EmptyClipBoard;
  2857. ClearDragFileList(FDragDropFilesEx.FileList);
  2858. AddToDragFileList(FDragDropFilesEx.FileList, Selected);
  2859. Result := FDragDropFilesEx.CopyToClipBoard;
  2860. LastClipBoardOperation := cboCopy;
  2861. end;
  2862. end; {CopyToClipBoard}
  2863. function TDriveView.CutToClipBoard(Node: TTreeNode): Boolean;
  2864. begin
  2865. Result := Assigned(Node) and Assigned(Node.Parent) and CopyToClipBoard(Node);
  2866. if Result then
  2867. begin
  2868. LastPathCut := NodePathName(Node);
  2869. LastClipBoardOperation := cboCut;
  2870. end;
  2871. end; {CutToClipBoard}
  2872. function TDriveView.CanPasteFromClipBoard: Boolean;
  2873. begin
  2874. Result := False;
  2875. if Assigned(Selected) and Windows.OpenClipboard(0) then
  2876. begin
  2877. Result := IsClipboardFormatAvailable(CF_HDROP);
  2878. Windows.CloseClipBoard;
  2879. end;
  2880. end; {CanPasteFromClipBoard}
  2881. procedure TDriveView.PasteFromClipBoard(Node: TTreeNode);
  2882. begin
  2883. ClearDragFileList(FDragDropFilesEx.FileList);
  2884. if CanPasteFromClipBoard and FDragDropFilesEx.GetFromClipBoard then
  2885. begin
  2886. case LastClipBoardOperation of
  2887. cboCopy,
  2888. cboNone:
  2889. begin
  2890. PerformDragDropFileOperation(Node, DROPEFFECT_COPY);
  2891. if Assigned(FOnDDExecuted) then
  2892. FOnDDExecuted(Self, DROPEFFECT_COPY);
  2893. end;
  2894. cboCut:
  2895. begin
  2896. PerformDragDropFileOperation(Node, DROPEFFECT_MOVE);
  2897. if Assigned(FOnDDExecuted) then
  2898. FOnDDExecuted(Self, DROPEFFECT_MOVE);
  2899. EmptyClipBoard;
  2900. end;
  2901. end;
  2902. end;
  2903. end; {PasteFromClipBoard}
  2904. procedure TDriveView.CMSysColorChange(var Message: TMessage);
  2905. begin
  2906. if not FValidateFlag then
  2907. begin
  2908. inherited;
  2909. end
  2910. else
  2911. begin
  2912. // Do not recreate the handle, if we are just iterating nodes, at that invalidates the node objects.
  2913. // This is not perfect, as the handle can be recreated for other reasons.
  2914. // But system color change is by far the most common case.
  2915. FSysColorChangePending := True;
  2916. end;
  2917. end;
  2918. end.