| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525 | unit DirView;{=============================================================== Component TDirView / Version 2.6, January 2000 ===============================================================    Description:    ============    Displays files of a single directory as listview with shell    icons. Complete drag&Drop support for files and directories.    Author:    =======    (c) Ingo Eckel 1998, 1999    Sodener Weg 38    65812 Bad Soden    Germany    Modifications (for WinSCP):    ===========================    (c) Martin Prikryl 2001- 2004    V2.6:    - Shows "shared"-symbol with directories    - Delphi5 compatible    For detailed documentation and history see TDirView.htm. ===============================================================}{Required compiler options for TDirView:}{$A+,B-,X+,H+,P+}interface{$WARN UNIT_PLATFORM OFF}{$WARN SYMBOL_PLATFORM OFF}uses  Windows, ShlObj, ComCtrls, CompThread, CustomDirView,  ExtCtrls, Graphics, FileOperator, DiscMon, Classes, DirViewColProperties,  DragDrop, Messages, ListViewColProperties, CommCtrl, DragDropFilesEx,  FileCtrl, SysUtils, BaseUtils, Controls, CustomDriveView, System.Generics.Collections, Winapi.ShellAPI;type  TVolumeDisplayStyle = (doPrettyName, doDisplayName); {Diplaytext of drive node}const  msThreadChangeDelay = 10; {TDiscMonitor: change delay}  MaxWaitTimeOut = 10; {TFileDeleteThread: wait nn seconds for deleting files or directories}{$WARN SYMBOL_DEPRECATED OFF}  FileAttr = SysUtils.faAnyFile and (not SysUtils.faVolumeID);{$WARN SYMBOL_DEPRECATED ON}  SpecialExtensions = 'EXE,LNK,ICO,ANI,CUR,PIF,JOB,CPL';  ExeExtension = 'EXE';type  {Exceptions:}  EDragDrop = class(Exception);  TClipboardOperation = (cboNone, cboCut, cboCopy);  {Record for each file item:}  PFileRec = ^TFileRec;  TFileRec = record    Empty: Boolean;    IconEmpty: Boolean;    IsDirectory: Boolean;    IsRecycleBin: Boolean;    IsParentDir: Boolean;    FileName: string;    Displayname: string;    FileExt: string;    TypeName: string;    ImageIndex: Integer;    Thumbnail: TBitmap;    ThumbnailSize: TSize;    Size: Int64;    Attr: LongWord;    FileTime: TFileTime;    PIDL: PItemIDList; {Fully qualified PIDL}    CalculatedSize: Int64;  end;{Additional events:}type  TDirViewFileSizeChanged = procedure(Sender: TObject; Item: TListItem) of object;  TDirViewFileIconForName = procedure(Sender: TObject; var FileName: string) of object;type  TDirView = class;  {  TIconUpdateThread (Fetch shell icons via thread) }  TIconUpdateThread = class(TCompThread)  private    FOwner: TDirView;    FSyncIcon: Integer;    FSyncThumbnail: TBitmap;    FCurrentIndex: Integer;    FCurrentFilePath: string;    FCurrentItemData: TFileRec;  protected    constructor Create(Owner: TDirView);    procedure Execute; override;  public    destructor Destroy; override;  end;  // WORKAROUND: TQueue<Integer>.Create fails when TDirView is created in IDE, while TQueue<TIconUpdateSchedule>.Create works  TIconUpdateSchedule = record    Index: Integer;  end;  { TDirView }  TDirView = class(TCustomDirView)  private    FConfirmDelete: Boolean;    FConfirmOverwrite: Boolean;    FDriveView: TCustomDriveView;    FChangeTimer: TTimer;    FChangeInterval: Cardinal;    FUseIconUpdateThread: Boolean;    FDriveType: Integer;    FParentFolder: IShellFolder;    FDesktopFolder: IShellFolder;    FDirOK: Boolean;    FPath: string;    SelectNewFiles: Boolean;    FHiddenCount: Integer;    FFilteredCount: Integer;    FNotRelative: Boolean;    {shFileOperation-shell component TFileOperator:}    FFileOperator: TFileOperator;    {Additional thread components:}    FIconUpdateThread: TIconUpdateThread;    // only ever accessed by GUI thread    FIconUpdateSet: TDictionary<Integer, Boolean>;    FIconUpdateQueue: TQueue<TIconUpdateSchedule>;    FIconUpdateQueueDeferred: TQueue<TIconUpdateSchedule>;    FDiscMonitor: TDiscMonitor;    FHomeDirectory: string;    {Additional events:}    FOnFileIconForName: TDirViewFileIconForName;    iRecycleFolder: iShellFolder;    PIDLRecycle: PItemIDList;    FLastPath: TDictionary<string, string>;    FTimeoutShellIconRetrieval: Boolean;    {Drag&Drop:}    function GetDirColProperties: TDirViewColProperties;    function GetHomeDirectory: string;    {Drag&drop helper functions:}    procedure SignalFileDelete(Sender: TObject; Files: TStringList);    procedure PerformDragDropFileOperation(TargetPath: string; Effect: Integer;      RenameOnCollision: Boolean; Paste: Boolean);    procedure SetDirColProperties(Value: TDirViewColProperties);  protected    function NewColProperties: TCustomListViewColProperties; override;    function SortAscendingByDefault(Index: Integer): Boolean; override;    procedure Notification(AComponent: TComponent; Operation: TOperation); override;    procedure Delete(Item: TListItem); override;    procedure DDError(ErrorNo: TDDError);    function GetCanUndoCopyMove: Boolean; virtual;    {Shell namespace functions:}    function GetShellFolder(Dir: string): iShellFolder;    function GetDirOK: Boolean; override;    procedure GetDisplayInfo(ListItem: TListItem; var DispInfo: TLVItem); override;    procedure DDDragDetect(grfKeyState: Longint; DetectStart, Point: TPoint;      DragStatus: TDragDetectStatus); override;    procedure DDMenuPopup(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;      AMinCustCmd:integer; grfKeyState: Longint; pt: TPoint); override;    procedure DDMenuDone(Sender: TObject; AMenu: HMenu); override;    procedure DDDropHandlerSucceeded(Sender: TObject; grfKeyState: Longint;      Point: TPoint; dwEffect: Longint); override;    procedure DDChooseEffect(grfKeyState: Integer; var dwEffect: Integer; PreferredEffect: Integer); override;    function GetPathName: string; override;    procedure SetChangeInterval(Value: Cardinal); virtual;    procedure LoadFromRecycleBin(Dir: string); virtual;    procedure SetLoadEnabled(Value: Boolean); override;    function GetPath: string; override;    procedure SetPath(Value: string); override;    procedure PathChanged; override;    procedure SetItemImageIndex(Item: TListItem; Index: Integer); override;    procedure ChangeDetected(Sender: TObject; const Directory: string;      var SubdirsChanged: Boolean);    procedure ChangeInvalid(Sender: TObject; const Directory: string; const ErrorStr: string);    procedure TimerOnTimer(Sender: TObject);    procedure ResetItemImage(Index: Integer);    procedure SetWatchForChanges(Value: Boolean); override;    procedure AddParentDirItem;    procedure AddToDragFileList(FileList: TFileList; Item: TListItem); override;    function DragCompleteFileList: Boolean; override;    procedure ExecuteFile(Item: TListItem); override;    function GetIsRoot: Boolean; override;    procedure InternalEdit(const HItem: TLVItem); override;    function ItemColor(Item: TListItem): TColor; override;    function ItemThumbnail(Item: TListItem; Size: TSize): TBitmap; override;    function ItemFileExt(Item: TListItem): string;    function ItemFileNameOnly(Item: TListItem): string;    function ItemImageIndex(Item: TListItem; Cache: Boolean): Integer; override;    function ItemIsFile(Item: TListItem): Boolean; override;    function ItemIsRecycleBin(Item: TListItem): Boolean; override;    function ItemMatchesFilter(Item: TListItem; const Filter: TFileFilter): Boolean; override;    function FileMatches(FileName: string; const SearchRec: TSearchRec): Boolean;    function ItemOverlayIndexes(Item: TListItem): Word; override;    procedure LoadFiles; override;    procedure PerformItemDragDropOperation(Item: TListItem; Effect: Integer; Paste: Boolean); override;    procedure SortItems; override;    function ThumbnailNeeded(ItemData: PFileRec): Boolean;    procedure DoFetchIconUpdate;    procedure DoUpdateIcon;    procedure StartFileDeleteThread;    procedure IconUpdateEnqueue(ListItem: TListItem);    function IconUpdatePeek: Integer;    procedure IconUpdateDequeue;    procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;    procedure Load(DoFocusSomething: Boolean); override;    procedure DoFetchIcon(      FilePath: string; IsSpecialExt: Boolean; CanTimeout: Boolean; FileRec: PFileRec; var ImageIndex: Integer; var TypeName: string);    function GetFileInfo(      CanUsePIDL: Boolean; PIDL: PItemIDList; Path: string; CanTimeout: Boolean;      dwFileAttributes: DWORD; var psfi: TSHFileInfoW; uFlags: UINT): DWORD_PTR;    function DoCopyToClipboard(Focused: Boolean; Cut: Boolean; Operation: TClipBoardOperation): Boolean;    function HiddenCount: Integer; override;    function FilteredCount: Integer; override;  public    {Runtime, readonly properties:}    property DriveType: Integer read FDriveType;    {Linked component TDriveView:}    property DriveView: TCustomDriveView read FDriveView write FDriveView;    { required, otherwise AV generated, when dragging columns}    property Columns stored False;    property ParentFolder: IShellFolder read FParentFolder;    {Drag&Drop runtime, readonly properties:}    property CanUndoCopyMove: Boolean read GetCanUndoCopyMove;    property DDFileOperator: TFileOperator read FFileOperator;    {Drag&Drop fileoperation methods:}    function UndoCopyMove: Boolean; dynamic;    {Clipboard fileoperation methods (requires drag&drop enabled):}    procedure EmptyClipboard; dynamic;    function CopyToClipBoard(Focused: Boolean): Boolean; dynamic;    function CutToClipBoard(Focused: Boolean): Boolean; dynamic;    function PasteFromClipBoard(TargetPath: string = ''): Boolean; override;    function DuplicateSelectedFiles: Boolean; dynamic;    procedure DisplayPropertiesMenu; override;    procedure DisplayContextMenu(Where: TPoint); override;    procedure ExecuteParentDirectory; override;    procedure ExecuteRootDirectory; override;    function ItemIsDirectory(Item: TListItem): Boolean; override;    function ItemFullFileName(Item: TListItem): string; override;    function ItemIsParentDirectory(Item: TListItem): Boolean; override;    function ItemFileName(Item: TListItem): string; override;    function ItemFileSize(Item: TListItem): Int64; override;    function ItemFileTime(Item: TListItem; var Precision: TDateTimePrecision): TDateTime; override;    procedure SetItemCalculatedSize(Item: TListItem; ASize: Int64); override;    procedure OpenFallbackPath(Value: string);    {Thread handling: }    procedure StartWatchThread;    procedure StopWatchThread;    function WatchThreadActive: Boolean;    procedure StartIconUpdateThread;    procedure StopIconUpdateThread;    procedure TerminateThreads;    {Create a new subdirectory:}    procedure CreateDirectory(DirName: string); override;    {Delete all selected files:}    {Check, if file or files still exists:}    procedure ValidateFile(Item: TListItem); overload;    procedure ValidateFile(FileName:TFileName); overload;    procedure ValidateSelectedFiles; dynamic;    {Access the internal data-structures:}    function AddItem(SRec: SysUtils.TSearchRec): TListItem; reintroduce;    procedure GetDisplayData(Item: TListItem; FetchIcon: Boolean);    function GetFileRec(Index: Integer): PFileRec;    {Populate / repopulate the filelist:}    procedure Reload(CacheIcons : Boolean); override;    procedure Reload2;    function GetAttrString(Attr: Integer): string; virtual;    constructor Create(AOwner: TComponent); override;    destructor Destroy; override;    procedure ExecuteHomeDirectory; override;    procedure ReloadDirectory; override;    procedure ExecuteDrive(Drive: string);    property HomeDirectory: string read GetHomeDirectory write FHomeDirectory;    property TimeoutShellIconRetrieval: Boolean read FTimeoutShellIconRetrieval write FTimeoutShellIconRetrieval;  published    property DirColProperties: TDirViewColProperties read GetDirColProperties write SetDirColProperties;    property PathLabel;    property OnUpdateStatusBar;    property DimmHiddenFiles;    property ShowHiddenFiles;    property WantUseDragImages;    property TargetPopupMenu;    property AddParentDir;    property OnSelectItem;    property OnStartLoading;    property OnLoaded;    property OnDDDragEnter;    property OnDDDragLeave;    property OnDDDragOver;    property OnDDDrop;    property OnDDQueryContinueDrag;    property OnDDGiveFeedback;    property OnDDDragDetect;    property OnDDCreateDragFileList;    property OnDDEnd;    property OnDDCreateDataObject;    property OnDDTargetHasDropHandler;    {Drag&Drop:}    property DDLinkOnExeDrag default True;    property OnDDProcessDropped;    property OnDDError;    property OnDDExecuted;    property OnDDFileOperation;    property OnDDFileOperationExecuted;    property OnExecFile;    property OnMatchMask;    property OnGetOverlay;    property OnGetItemColor;    {Confirm deleting files}    property ConfirmDelete: Boolean      read FConfirmDelete write FConfirmDelete default True;    {Confirm overwriting files}    property ConfirmOverwrite: Boolean      read FConfirmOverwrite write fConfirmOverwrite default True;    {Reload the directory after only the interval:}    property ChangeInterval: Cardinal      read FChangeInterval write SetChangeInterval default MSecsPerSec;    {Fetch shell icons by thread:}    property UseIconUpdateThread: Boolean      read FUseIconUpdateThread write FUseIconUpdateThread default False;    {Watch current directory for filename changes (create, rename, delete files)}    property WatchForChanges;    {Additional events:}    property OnFileIconForName: TDirViewFileIconForName      read FOnFileIconForName write FOnFileIconForName;    property UseSystemContextMenu;    property OnContextPopup;    property OnHistoryChange;    property OnHistoryGo;    property OnPathChange;    property OnBusy;    property OnChangeFocus;    property ColumnClick;    property MultiSelect;    property ReadOnly;    property DirViewStyle;    // The only way to make Items stored automatically and survive handle recreation.    // Though we should implement custom persisting to avoid publishing this    property Items;  end; {Type TDirView}procedure Register;{Returns True, if the specified extension matches one of the extensions in ExtList:}function MatchesFileExt(Ext: string; const FileExtList: string): Boolean;function DropLink(Item: PFDDListItem; TargetPath: string): Boolean;function DropFiles(  DragDropFilesEx: TCustomizableDragDropFilesEx; Effect: Integer; FileOperator: TFileOperator; TargetPath: string;  RenameOnCollision: Boolean; IsRecycleBin: Boolean; ConfirmDelete: Boolean; ConfirmOverwrite: Boolean; Paste: Boolean;  Sender: TObject; OnDDFileOperation: TDDFileOperationEvent;  out SourcePath: string; out SourceIsDirectory: Boolean): Boolean;procedure CheckCanOpenDirectory(Path: string);var  LastClipBoardOperation: TClipBoardOperation;implementationuses  DriveView, OperationWithTimeout,  PIDL, Forms, Dialogs,  ComObj,  ActiveX, ImgList,  ShellDialogs, IEDriveInfo,  FileChanges, Math, PasTools, StrUtils, Types, UITypes;var  ThumbnailNotNeeded: TSize;var  DaylightHack: Boolean;procedure Register;begin  RegisterComponents('DriveDir', [TDirView]);end; {Register}function MatchesFileExt(Ext: string; const FileExtList: string): Boolean;begin  Result := (Length(Ext) = 3) and (Pos(Ext, FileExtList) <> 0);end; {MatchesFileExt}function FileTimeToDateTime(FileTime: TFileTime): TDateTime;var  SysTime: TSystemTime;  UniverzalSysTime: TSystemTime;  LocalFileTime: TFileTime;begin  // duplicated in Common.cpp  // The 0xFFF... is sometime seen for invalid timestamps,  // it would cause failure in SystemTimeToDateTime below  if FileTime.dwLowDateTime = High(DWORD) then  begin    Result := MinDateTime;  end    else  begin    if not DaylightHack then    begin      FileTimeToSystemTime(FileTime, UniverzalSysTime);      SystemTimeToTzSpecificLocalTime(nil, UniverzalSysTime, SysTime);    end      else    begin      FileTimeToLocalFileTime(FileTime, LocalFileTime);      FileTimeToSystemTime(LocalFileTime, SysTime);    end;    Result := SystemTimeToDateTime(SysTime);  end;end;function SizeFromSRec(const SRec: SysUtils.TSearchRec): Int64;begin  with SRec do  begin    // Hopefuly TSearchRec.FindData is available with all Windows versions    {if Size >= 0 then Result := Size      else}{$WARNINGS OFF}    Result := Int64(FindData.nFileSizeHigh) shl 32 + FindData.nFileSizeLow;{$WARNINGS ON}  end;end;function DropLink(Item: PFDDListItem; TargetPath: string): Boolean;var  Drive: string;  SourcePath: string;  SourceFile: string;begin  SourceFile := Item.Name;  if IsRootPath(SourceFile) then  begin    Drive := DriveInfo.GetDriveKey(SourceFile);    SourcePath := Copy(DriveInfo.Get(Drive).PrettyName, 4, 255) + ' (' + Drive + ')'  end    else  begin    SourcePath := ExtractFileName(SourceFile);  end;  Result :=    CreateFileShortCut(SourceFile,      IncludeTrailingBackslash(TargetPath) + ChangeFileExt(SourcePath, '.lnk'),      ExtractFileNameOnly(SourceFile));end;function DropFiles(  DragDropFilesEx: TCustomizableDragDropFilesEx; Effect: Integer; FileOperator: TFileOperator; TargetPath: string;  RenameOnCollision: Boolean; IsRecycleBin: Boolean; ConfirmDelete: Boolean; ConfirmOverwrite: Boolean; Paste: Boolean;  Sender: TObject; OnDDFileOperation: TDDFileOperationEvent;  out SourcePath: string; out SourceIsDirectory: Boolean): Boolean;var  Index: Integer;  DoFileOperation: Boolean;begin  SourcePath := '';  {Set the source filenames:}  for Index := 0 to DragDropFilesEx.FileList.Count - 1 do  begin    FileOperator.OperandFrom.Add(      TFDDListItem(DragDropFilesEx.FileList[Index]^).Name);    if DragDropFilesEx.FileNamesAreMapped then      FileOperator.OperandTo.Add(IncludeTrailingPathDelimiter(TargetPath) +        TFDDListItem(DragDropFilesEx.FileList[Index]^).MappedName);    if SourcePath = '' then    begin      if DirectoryExists(TFDDListItem(DragDropFilesEx.FileList[Index]^).Name) then      begin        SourcePath := TFDDListItem(DragDropFilesEx.FileList[Index]^).Name;        SourceIsDirectory := True;      end        else      begin        SourcePath := ExtractFilePath(TFDDListItem(DragDropFilesEx.FileList[Index]^).Name);        SourceIsDirectory := False;      end;    end;  end;  FileOperator.Flags := FileOperatorDefaultFlags;  if RenameOnCollision then  begin    FileOperator.Flags := FileOperator.Flags + [foRenameOnCollision];    FileOperator.WantMappingHandle := True;  end    else FileOperator.WantMappingHandle := False;  {Set the target directory or the target filenames:}  if DragDropFilesEx.FileNamesAreMapped and (not IsRecycleBin) then  begin    FileOperator.Flags := FileOperator.Flags + [foMultiDestFiles];  end    else  begin    FileOperator.Flags := FileOperator.Flags - [foMultiDestFiles];    FileOperator.OperandTo.Clear;    FileOperator.OperandTo.Add(TargetPath);  end;  {if the target directory is the recycle bin, then delete the selected files:}  if IsRecycleBin then  begin    FileOperator.Operation := foDelete;  end    else  begin    case Effect of      DROPEFFECT_COPY: FileOperator.Operation := foCopy;      DROPEFFECT_MOVE: FileOperator.Operation := foMove;    end;  end;  if IsRecycleBin then  begin    if not ConfirmDelete then      FileOperator.Flags := FileOperator.Flags + [foNoConfirmation];  end    else  begin    if not ConfirmOverwrite then      FileOperator.Flags := FileOperator.Flags + [foNoConfirmation];  end;  DoFileOperation := True;  if Assigned(OnDDFileOperation) then  begin    OnDDFileOperation(Sender, Effect, SourcePath, TargetPath, False, DoFileOperation);  end;  Result := DoFileOperation and (FileOperator.OperandFrom.Count > 0);  if Result then  begin    FileOperator.Execute;    if DragDropFilesEx.FileNamesAreMapped then      FileOperator.ClearUndo;  end;end;function GetShellDisplayName(  const ShellFolder: IShellFolder; IDList: PItemIDList; Flags: DWORD; var Name: string): Boolean;var  Str: TStrRet;begin  Result := True;  Name := '';  if ShellFolder.GetDisplayNameOf(IDList, Flags, Str) = NOERROR then  begin    case Str.uType of      STRRET_WSTR: Name := WideCharToString(Str.pOleStr);      STRRET_OFFSET: Name := PChar(UINT(IDList) + Str.uOffset);      STRRET_CSTR: Name := string(Str.cStr);      else Result := False;    end;  end    else Result := False;end; {GetShellDisplayName}procedure CheckCanOpenDirectory(Path: string);var  DosError: Integer;  SRec: SysUtils.TSearchRec;begin  if not DirectoryExistsFix(Path) then  begin    DosError := GetLastError;    if (DosError = ERROR_FILE_NOT_FOUND) or (DosError = ERROR_PATH_NOT_FOUND) then      raise Exception.CreateFmt(SDirNotExists, [Path])    else      RaiseLastOSError;  end;  DosError := SysUtils.FindFirst(ApiPath(IncludeTrailingPathDelimiter(Path) + '*.*'), FileAttr, SRec);  if DosError = ERROR_SUCCESS then  begin    FindClose(SRec);  end    else  begin    // File not found is expected when accessing a root folder of an empty drive    if DosError <> ERROR_FILE_NOT_FOUND then    begin      RaiseLastOSError;    end;  end;end;{ TIconUpdateThread }constructor TIconUpdateThread.Create(Owner: TDirView);begin  inherited Create(True);  FOwner := Owner;end; {TIconUpdateThread.Create}destructor TIconUpdateThread.Destroy;begin  FreeAndNil(FSyncThumbnail);  inherited;end;procedure TIconUpdateThread.Execute;var  IsSpecialExt: Boolean;  TypeName: string;  PrevPIDL: PItemIDList;begin  CoInitialize(nil); // needed for SHGetFileInfo  while not Terminated do  begin    FCurrentIndex := -1;    Synchronize(FOwner.DoFetchIconUpdate);    if (FCurrentIndex >= 0) and       (not Terminated) then    begin      FSyncIcon := -1;      if Assigned(FSyncThumbnail) then        FreeAndNil(FSyncThumbnail);      if FCurrentItemData.IconEmpty then      begin        PrevPIDL := FCurrentItemData.PIDL;        try          IsSpecialExt := MatchesFileExt(FCurrentItemData.FileExt, SpecialExtensions);          FOwner.DoFetchIcon(FCurrentFilePath, IsSpecialExt, False, @FCurrentItemData, FSyncIcon, TypeName);        except          {Capture exceptions generated by the shell}          FSyncIcon := UnknownFileIcon;        end;        if Assigned(FCurrentItemData.PIDL) and           (PrevPIDL <> FCurrentItemData.PIDL) then        begin          FreePIDL(FCurrentItemData.PIDL);        end;      end;      if FOwner.ThumbnailNeeded(@FCurrentItemData) then      begin        FSyncThumbnail := GetThumbnail(FCurrentFilePath, FCurrentItemData.ThumbnailSize);      end;      if not Terminated then      begin        Synchronize(FOwner.DoUpdateIcon);      end;    end;    SetLength(FCurrentFilePath, 0);  end;end; {TIconUpdateThread.Execute}{ TDirView }constructor TDirView.Create(AOwner: TComponent);begin  inherited Create(AOwner);  FDriveType := DRIVE_UNKNOWN;  FConfirmDelete := True;  FParentFolder := nil;  FDesktopFolder := nil;  SelectNewFiles := False;  DragOnDriveIsMove := True;  FHiddenCount := 0;  FFilteredCount := 0;  FNotRelative := False;  FFileOperator := TFileOperator.Create(Self);  FDirOK := True;  FPath := '';  FDiscMonitor := nil;  {ChangeTimer: }  if FChangeInterval = 0 then FChangeInterval := MSecsPerSec;  FChangeTimer := TTimer.Create(Self);  FChangeTimer.Interval := FChangeInterval;  FChangeTimer.Enabled := False;  FChangeTimer.OnTimer := TimerOnTimer;  {Drag&drop:}  FConfirmOverwrite := True;  DDLinkOnExeDrag := True;  with DragDropFilesEx do  begin    SourceEffects := DragSourceEffects;    TargetEffects := [deCopy, deMove, deLink];    ShellExtensions.DragDropHandler := True;    ShellExtensions.DropHandler := True;  end;  FIconUpdateSet := TDictionary<Integer, Boolean>.Create;  FIconUpdateQueue := TQueue<TIconUpdateSchedule>.Create;  FIconUpdateQueueDeferred := TQueue<TIconUpdateSchedule>.Create;  FLastPath := nil;end; {Create}destructor TDirView.Destroy;begin  FreeAndNil(FIconUpdateQueue);  FreeAndNil(FIconUpdateQueueDeferred);  FreeAndNil(FIconUpdateSet);  if Assigned(PIDLRecycle) then FreePIDL(PIDLRecycle);  FLastPath.Free;  FFileOperator.Free;  FChangeTimer.Free;  inherited Destroy;  FPath := '';end; {Destroy}procedure TDirView.WMDestroy(var Msg: TWMDestroy);begin  Selected := nil;  ClearItems;  TerminateThreads;  inherited;end; {WMDestroy}procedure TDirView.TerminateThreads;begin  StopIconUpdateThread;  StopWatchThread;  if Assigned(FDiscMonitor) then  begin    FDiscMonitor.Free;    FDiscMonitor := nil;  end;end; {TerminateThreads}function TDirView.GetHomeDirectory: string;begin  if FHomeDirectory <> '' then Result := FHomeDirectory    else  begin    Result := UserDocumentDirectory;    // in rare case the CSIDL_PERSONAL cannot be resolved    if Result = '' then    begin      Result := DriveInfo.AnyValidPath;    end;  end;end; { GetHomeDirectory }function TDirView.GetIsRoot: Boolean;begin  Result := IsRootPath(Path);end;function TDirView.GetPath: string;begin  Result := FPath;end;procedure TDirView.PathChanged;var  Expanded: string;begin  inherited;  // make sure to use PathName as Path maybe just X: what  // ExpandFileName resolves to current working directory  // on the drive, not to root path  Expanded := ExpandFileName(PathName);  if not Assigned(FLastPath) then  begin    FLastPath := TDictionary<string, string>.Create;  end;  FLastPath.AddOrSetValue(DriveInfo.GetDriveKey(Expanded), Expanded);end;procedure TDirView.SetPath(Value: string);const  LongPathPrefix = '\\?\';var  LongPath: string;  Len: Integer;begin  // do checks before passing directory to drive view, because  // it would truncate non-existing directory to first superior existing  Value := ReplaceStr(Value, '/', '\');  // GetLongPathName would resolve to it the current working directory  if (Length(Value) = 2) and CharInSet(UpperCase(Value)[1], ['A'..'Z']) and (Value[2] = ':') then  begin    Value := Value + '\';  end    else  // Convert to long path  begin    Len := GetLongPathName(PChar(ApiPath(Value)), nil, 0);    SetLength(LongPath, Len);    Len := GetLongPathName(PChar(ApiPath(Value)), PChar(LongPath), Len);    if Len > 0 then    begin      Value := Copy(LongPath, 1, Len);      if StartsStr(LongPathPrefix, Value) then        System.Delete(Value, 1, Length(LongPathPrefix));    end;  end;  CheckCanOpenDirectory(Value);  if Assigned(FDriveView) and     (FDriveView.Directory <> Value) then  begin    FDriveView.Directory := Value;  end    else  if FPath <> Value then  try    while ExcludeTrailingPathDelimiter(Value) <> Value do    begin      Value := ExcludeTrailingPathDelimiter(Value);    end;    PathChanging(not FNotRelative);    FPath := Value;    Load(True);  finally    PathChanged;  end;end;procedure TDirView.OpenFallbackPath(Value: string);var  APath: string;begin  while True do  begin    APath := ExtractFileDir(Value);    if (APath = '') or (APath = Value) then    begin      Break;    end      else    begin      try        Path := APath;        Break;      except        Value := APath;      end;    end;  end;end;procedure TDirView.SetLoadEnabled(Value: Boolean);begin  if Value <> LoadEnabled then  begin    FLoadEnabled := Enabled;    if LoadEnabled and Dirty then    begin      if Items.Count > 100 then Reload2        else Reload(True);    end;  end;end; {SetLoadEnabled}function TDirView.GetPathName: string;begin  if IsRoot then Result := IncludeTrailingBackslash(Path)    else Result := Path;end; {GetPathName}function TDirView.GetFileRec(Index: Integer): PFileRec;begin  if Index > Pred(Items.Count) then Result := nil    else Result := Items[index].Data;end; {GetFileRec}function TDirView.HiddenCount: Integer;begin  Result := FHiddenCount;end;function TDirView.FilteredCount: Integer;begin  Result := FFilteredCount;end;function TDirView.AddItem(SRec: SysUtils.TSearchRec): TListItem;var  PItem: PFileRec;  Item: TListItem;begin  Item := TListItem.Create(Items);  New(PItem);  with PItem^ do  begin    // must be set as soon as possible, at least before Caption is set,    // because if come column is "autosized" setting Caption invokes some callbacks    Item.Data := PItem;    FileName := SRec.Name;    FileExt := UpperCase(ExtractFileExt(Srec.Name));    FileExt := Copy(FileExt, 2, Length(FileExt) - 1);    DisplayName := FileName;{$WARNINGS OFF}    Attr := SRec.FindData.dwFileAttributes;{$WARNINGS ON}    IsParentDir := False;    IsDirectory := ((Attr and SysUtils.faDirectory) <> 0);    IsRecycleBin := IsDirectory and (Length(Path) = 2) and      Bool(Attr and SysUtils.faSysFile) and      ((UpperCase(FileName) = 'RECYCLED') or (UpperCase(FileName) = 'RECYCLER'));    if not IsDirectory then Size := SizeFromSRec(SRec)      else Size := -1;{$WARNINGS OFF}    FileTime := SRec.FindData.ftLastWriteTime;{$WARNINGS ON}    Empty := True;    IconEmpty := True;    Thumbnail := nil;    ThumbnailSize := ThumbnailNotNeeded;    if Size > 0 then Inc(FFilesSize, Size);    PIDL := nil;    CalculatedSize := -1;    // Need to add before assigning to .Caption and .OverlayIndex,    // as the setters these call back to owning view.    // Assignment is redundant    Item := Items.AddItem(Item);    if not Self.IsRecycleBin then Item.Caption := SRec.Name;    if FileExt = 'LNK' then Item.OverlayIndex := 1;  end;  if SelectNewFiles then Item.Selected := True;  Result := Item;end; {AddItem}procedure TDirView.AddParentDirItem;var  PItem: PFileRec;  Item: TListItem;  SRec: SysUtils.TSearchRec;begin  FHasParentDir := True;  Item := Items.Add;  New(PItem);  if FindFirst(ApiPath(FPath), faAnyFile, SRec) = 0 then    FindClose(SRec);  with PItem^ do  begin    Item.Data := PItem;    FileName := '..';    FileExt := '';    DisplayName := '..';    Attr := SRec.Attr;    IsDirectory := True;    IsRecycleBin := False;    IsParentDir := True;    Size := -1;    CalculatedSize := -1;    Item.Caption := '..';{$WARNINGS OFF}    FileTime := SRec.FindData.ftLastWriteTime;{$WARNINGS ON}    Empty := True;    IconEmpty := False;    Thumbnail := nil;    ThumbnailSize := ThumbnailNotNeeded;    PIDL := nil;    ImageIndex := StdDirIcon;    TypeName := SParentDir;    Empty := False;  end;end; {AddParentDirItem}procedure TDirView.LoadFromRecycleBin(Dir: string);var  PIDLRecycleLocal: PItemIDList;  PCurrList: PItemIDList;  FQPIDL: PItemIDList;  EnumList: IEnumIDList;  Fetched: ULONG;  SRec: SysUtils.TSearchRec;  DisplayName: string;  FullPath: string;  NewItem: TListItem;  FileRec: PFileRec;  FileInfo: TSHFileInfo;  DosError: Integer;begin  if not Assigned(iRecycleFolder) then  begin    PIDLRecycleLocal := nil;    try      OLECheck(SHGetSpecialFolderLocation(Self.Handle,        CSIDL_BITBUCKET, PIDLRecycleLocal));      PIDLRecycle := PIDL_Concatenate(nil, PIDLRecycleLocal);      if not SUCCEEDED(FDesktopFolder.BindToObject(PIDLRecycle, nil,        IID_IShellFolder, Pointer(iRecycleFolder))) then Exit;    finally      if Assigned(PIDLRecycleLocal) then        FreePIDL(PIDLRecycleLocal);    end;  end;  FParentFolder := iRecycleFolder;  if AddParentDir then AddParentDirItem;  FHiddenCount := 0;  FFilteredCount := 0;  if SUCCEEDED(iRecycleFolder.EnumObjects(Self.Handle,    SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, EnumList)) then  begin     while (EnumList.Next(1, PCurrList, Fetched) = S_OK) and not AbortLoading do     begin       if Assigned(PCurrList) then       try         FQPIDL := PIDL_Concatenate(PIDLRecycle, PCurrList);         {Physical filename:}         SetLength(FullPath, MAX_PATH);         if shGetPathFromIDList(FQPIDL, PChar(FullPath)) then           SetLength(FullPath, StrLen(PChar(FullPath)));         {Filesize, attributes and -date:}         DosError := FindFirst(ApiPath(FullPath), faAnyFile, SRec);         FindClose(Srec);         SRec.Name := ExtractFilePath(FullPath) + SRec.Name;         {Displayname:}         GetShellDisplayName(iRecycleFolder, PCurrList, SHGDN_FORPARSING, DisplayName);         if (DosError = 0) and            (((SRec.Attr and faDirectory) <> 0) or             FileMatches(DisplayName, SRec)) then         begin           {Filetype and icon:}           GetFileInfo(True, FQPIDL, '', False, 0, FileInfo, SHGFI_TYPENAME or SHGFI_SYSICONINDEX);           NewItem := AddItem(Srec);           NewItem.Caption := DisplayName;           FileRec := NewItem.Data;           FileRec^.Empty := False;           FileRec^.IconEmpty := False;           FileRec^.DisplayName := DisplayName;           FileRec^.PIDL := FQPIDL;           FileRec^.TypeName := FileInfo.szTypeName;           if FileRec^.Typename = EmptyStr then             FileRec^.TypeName := Format(STextFileExt, [FileRec.FileExt]);           FileRec^.ImageIndex := FileInfo.iIcon;         end           else         begin           FreePIDL(FQPIDL);         end;         FreePIDL(PCurrList);       except         if Assigned(PCurrList) then         try           FreePIDL(PCurrList);         except         end;       end;     end; {While EnumList ...}  end;end; {LoadFromRecycleBin}function TDirView.GetShellFolder(Dir: string): iShellFolder;var  Eaten: ULONG;  Attr: ULONG;  NewPIDL: PItemIDList;begin  Result := nil;  if not Assigned(FDesktopFolder) then    SHGetDesktopFolder(FDesktopFolder);  if Assigned(FDesktopFolder) then  begin    Attr := 0;    if Succeeded(FDesktopFolder.ParseDisplayName(         ParentForm.Handle, nil, PChar(Dir), Eaten, NewPIDL, Attr)) then    begin      try        assert(Assigned(NewPIDL));        FDesktopFolder.BindToObject(NewPIDL, nil, IID_IShellFolder, Pointer(Result));        Assert(Assigned(Result));      finally        FreePIDL(NewPIDL);      end;    end;  end;end; {GetShellFolder}function TDirView.ItemIsDirectory(Item: TListItem): Boolean;begin  Result :=    (Assigned(Item) and Assigned(Item.Data) and    PFileRec(Item.Data)^.IsDirectory);end;function TDirView.ItemIsFile(Item: TListItem): Boolean;begin  Result :=    (Assigned(Item) and Assigned(Item.Data) and     (not PFileRec(Item.Data)^.IsParentDir));end;function TDirView.ItemIsParentDirectory(Item: TListItem): Boolean;begin  Result :=    (Assigned(Item) and Assigned(Item.Data) and    PFileRec(Item.Data)^.IsParentDir);end;function TDirView.ItemIsRecycleBin(Item: TListItem): Boolean;begin  Result := (Assigned(Item) and Assigned(Item.Data) and    PFileRec(Item.Data)^.IsRecycleBin);end;function TDirView.ItemMatchesFilter(Item: TListItem; const Filter: TFileFilter): Boolean;var  FileRec: PFileRec;begin  Assert(Assigned(Item) and Assigned(Item.Data));  FileRec := PFileRec(Item.Data);  Result :=    ((Filter.Masks = '') or     FileNameMatchesMasks(FileRec^.FileName, FileRec^.IsDirectory,       FileRec^.Size, FileTimeToDateTime(FileRec^.FileTime), Filter.Masks, False) or     (FileRec^.IsDirectory and Filter.Directories and      FileNameMatchesMasks(FileRec^.FileName, False,       FileRec^.Size, FileTimeToDateTime(FileRec^.FileTime), Filter.Masks, False)));end;function TDirView.FileMatches(FileName: string; const SearchRec: TSearchRec): Boolean;var  Directory: Boolean;  FileSize: Int64;begin  Result := (ShowHiddenFiles or ((SearchRec.Attr and SysUtils.faHidden) = 0));  if not Result then  begin    Inc(FHiddenCount);  end    else  if FEffectiveMask <> '' then  begin    Directory := ((SearchRec.Attr and faDirectory) <> 0);    if Directory then FileSize := 0      else FileSize := SizeFromSRec(SearchRec);    Result :=      FileNameMatchesMasks(        FileName,        Directory,        FileSize,        FileTimeToDateTime(SearchRec.FindData.ftLastWriteTime),        FEffectiveMask, True);    if not Result then    begin      Inc(FFilteredCount);    end;  end;end;function TDirView.ItemOverlayIndexes(Item: TListItem): Word;begin  Result := inherited ItemOverlayIndexes(Item);  if Assigned(Item) and Assigned(Item.Data) then  begin    if PFileRec(Item.Data)^.IsParentDir then      Inc(Result, oiDirUp);  end;end;procedure TDirView.Load(DoFocusSomething: Boolean);begin  try    StopIconUpdateThread;    FIconUpdateQueue.Clear;    FIconUpdateQueueDeferred.Clear;    FIconUpdateSet.Clear;    StopWatchThread;    FChangeTimer.Enabled  := False;    FChangeTimer.Interval := 0;    inherited;  finally    if DirOK and not AbortLoading then    begin      if not IsRecycleBin then        StartIconUpdateThread;      StartWatchThread;    end;  end;end;procedure TDirView.LoadFiles;var  SRec: SysUtils.TSearchRec;  DosError: Integer;  DirsCount: Integer;  SelTreeNode: TTreeNode;  Node: TTreeNode;  Drive: string;begin  FHiddenCount := 0;  FFilteredCount := 0;  try    if Length(FPath) > 0 then    begin      Drive := DriveInfo.GetDriveKey(FPath);      DriveInfo.ReadDriveStatus(Drive, dsSize);      FDriveType := DriveInfo.Get(Drive).DriveType;      FDirOK := DriveInfo.Get(Drive).DriveReady and DirectoryExists(ApiPath(FPath));    end      else    begin      FDriveType := DRIVE_UNKNOWN;      FDirOK := False;    end;    if DirOK then    begin      if Assigned(FDriveView) then        SelTreeNode := TDriveView(FDriveView).FindNodeToPath(FPath)        else SelTreeNode := nil;      if Assigned(FDriveView) and Assigned(SelTreeNode) then          FIsRecycleBin := TNodeData(SelTreeNode.Data).IsRecycleBin        else      FIsRecycleBin :=        (Uppercase(Copy(FPath, 2, 10)) = ':\RECYCLED') or        (Uppercase(Copy(FPath, 2, 10)) = ':\RECYCLER');      if not Assigned(FDesktopFolder) then        SHGetDesktopFolder(FDesktopFolder);      if IsRecycleBin then LoadFromRecycleBin(Path)        else      begin        FParentFolder := GetShellFolder(PathName);        DosError :=          FindFirstEx(ApiPath(IncludeTrailingPathDelimiter(FPath) + '*.*'), FileAttr, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS);        while (DosError = 0) and (not AbortLoading) do        begin          if (SRec.Attr and faDirectory) = 0 then          begin            if FileMatches(SRec.Name, SRec) then            begin              AddItem(SRec);            end;          end;          DosError := FindNext(SRec);        end;        SysUtils.FindClose(SRec);        if AddParentDir and (not IsRoot) then        begin          AddParentDirItem;        end;        {Search for directories:}        DirsCount := 0;        DosError :=          FindFirstEx(ApiPath(IncludeTrailingPathDelimiter(FPath) + '*.*'), DirAttrMask, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS);        while (DosError = 0) and (not AbortLoading) do        begin          if (SRec.Name <> '.') and (SRec.Name <> '..') and             ((Srec.Attr and faDirectory) <> 0) then          begin            Inc(DirsCount);            if FileMatches(SRec.Name, SRec) then            begin              AddItem(Srec);            end;          end;          DosError := FindNext(SRec);        end;        SysUtils.FindClose(SRec);        {Update TDriveView's subdir indicator:}        if Assigned(FDriveView) and (FDriveType = DRIVE_REMOTE) then          with TDriveView(FDriveView) do          begin            Node := FindNodeToPath(PathName);            if Assigned(Node) and Assigned(Node.Data) and               not TNodeData(Node.Data).Scanned then            begin              if DirsCount = 0 then              begin                Node.HasChildren := False;                TNodeData(Node.Data).Scanned := True;              end;            end;          end;      end; {not isRecycleBin}    end      else FIsRecycleBin := False;  finally  end; {Finally}end;procedure TDirView.Reload2;type  PEFileRec = ^TEFileRec;  TEFileRec = record    iSize: Int64;    iAttr: Integer;    iFileTime: TFileTime;    iIndex: Integer;  end;var  Index: Integer;  EItems: TStringList;  FItems: TStringList;  NewItems: TStringList;  Srec: SysUtils.TSearchRec;  DosError: Integer;  PSrec: ^SysUtils.TSearchRec;  Dummy: Integer;  ItemIndex: Integer;  AnyUpdate: Boolean;  PUpdate: Boolean;  PEFile: PEFileRec;  SaveCursor: TCursor;  FSize: Int64;  FocusedIsVisible: Boolean;begin  if (not Loading) and LoadEnabled then  begin    if IsRecycleBin then Reload(True)      else    begin      if not DirectoryExists(Path) then      begin        ClearItems;        FDirOK := False;        FDirty := False;      end        else      begin        FocusedIsVisible := Assigned(ItemFocused) and IsItemVisible(ItemFocused);        SaveCursor := Screen.Cursor;        Screen.Cursor := crHourGlass;        FChangeTimer.Enabled  := False;        FChangeTimer.Interval := 0;        EItems := TStringlist.Create;        EItems.CaseSensitive := True; // We want to reflect changes in file name case        FItems := TStringlist.Create;        FItems.CaseSensitive := True;        NewItems := TStringlist.Create;        PUpdate := False;        AnyUpdate := False;        FHiddenCount := 0;        FFilteredCount := 0;        try          {Store existing files and directories:}          for Index := 0 to Items.Count - 1 do          begin            New(PEFile);            with PFileRec(Items[Index].Data)^ do            begin              PEFile^.iSize := Size;              PEFile^.iAttr := Attr;              PEFile^.iFileTime := FileTime;              PEFile^.iIndex := Index;            end;            EItems.AddObject(PFileRec(Items[Index].Data)^.FileName, Pointer(PEFile));          end;          EItems.Sort;          DosError :=            FindFirstEx(ApiPath(IncludeTrailingPathDelimiter(FPath) + '*.*'), FileAttr, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS);          while DosError = 0 do          begin            if (SRec.Attr and faDirectory) = 0 then            begin              if FileMatches(SRec.Name, SRec) then              begin                ItemIndex := -1;                if not EItems.Find(SRec.Name, ItemIndex) then                begin                  New(PSrec);                  PSRec^ := SRec;                  NewItems.AddObject(SRec.Name, Pointer(PSrec));                  FItems.Add(Srec.Name);                end                  else                begin                  FSize := SizeFromSRec(SRec);                  with PEFileRec(EItems.Objects[ItemIndex])^ do{$WARNINGS OFF}                  if (iSize <> FSize) or (iAttr <> SRec.Attr) or                     not CompareMem(@iFileTime, @SRec.FindData.ftLastWriteTime,                        SizeOf(iFileTime)) Then{$WARNINGS ON}                  begin                    with PFileRec(Items[iIndex].Data)^ do                    begin                      Dec(FFilesSize, Size);                      Inc(FFilesSize, FSize);                      if Items[iIndex].Selected then                      begin                        Dec(FFilesSelSize, Size);                        Inc(FFilesSelSize, FSize);                      end;                      Size := FSize;                      Attr := SRec.Attr;{$WARNINGS OFF}                      FileTime := SRec.FindData.ftLastWriteTime;{$WARNINGS ON}                    end;                    InvalidateItem(Items[iIndex]);                    AnyUpdate := True;                  end;                  FItems.Add(Srec.Name);                end;              end;            end;            DosError := FindNext(Srec);          end;          SysUtils.FindClose(Srec);          {Search new directories:}          DosError :=            FindFirstEx(ApiPath(FPath + '\*.*'), DirAttrMask, SRec, FIND_FIRST_EX_LARGE_FETCH_PAS);          while DosError = 0 do          begin            if (Srec.Attr and faDirectory) <> 0 then            begin              if (SRec.Name <> '.') and (SRec.Name <> '..') then              begin                if not EItems.Find(SRec.Name, ItemIndex) then                begin                  if FileMatches(SRec.Name, SRec) then                  begin                    New(PSrec);                    PSrec^ := SRec;                    NewItems.AddObject(Srec.Name, Pointer(PSrec));                    FItems.Add(SRec.Name);                  end;                end                  else                begin                  FItems.Add(SRec.Name);                end;              end                else              begin                FItems.Add(SRec.Name);              end;            end;            DosError := FindNext(SRec);          end;          SysUtils.FindClose(SRec);          {Check wether displayed Items still exists:}          FItems.Sort;          for Index := Items.Count - 1 downto 0 do          begin            if not FItems.Find(PFileRec(Items[Index].Data)^.FileName, Dummy) then            begin              if not PUpdate then              begin                PUpdate := True;                Items.BeginUpdate;              end;              AnyUpdate := True;              with PFileRec(Items[Index].Data)^ do              begin                Dec(FFilesSize, Size);                // No need to decrease FFilesSelSize here as LVIF_STATE/deselect                // is called for item being deleted              end;              Items[Index].Delete;            end;          end;        finally          try            for Index := 0 to EItems.Count - 1  do              Dispose(PEFileRec(EItems.Objects[Index]));            EItems.Free;            FItems.Free;            for Index := 0 to NewItems.Count - 1 do            begin              if not PUpdate then              begin                PUpdate := True;                Items.BeginUpdate;              end;              AnyUpdate := True;              PSrec := Pointer(NewItems.Objects[Index]);              AddItem(PSrec^);              Dispose(PSrec);            end;            NewItems.Free;            // if we are sorted by name and there were only updates to existing            // items, there is no need for sorting            if PUpdate or               (AnyUpdate and (DirColProperties.SortDirColumn <> dvName)) then            begin              SortItems;            end;            if PUpdate then              Items.EndUpdate;          finally            FDirOK := True;            FDirty := false;            if not FIsRecycleBin then              StartIconUpdateThread;            StartWatchThread;            // make focused item visible, only if it was before            if FocusedIsVisible and Assigned(ItemFocused) then              ItemFocused.MakeVisible(False);            DoUpdateStatusBar;            Screen.Cursor := SaveCursor;          end;        end; {Finally}      end;      if Assigned(FDriveView) then      begin        TDriveView(FDriveView).ValidateCurrentDirectoryIfNotMonitoring;      end;    end;  end;end; {Reload2}procedure TDirView.PerformItemDragDropOperation(Item: TListItem; Effect: Integer; Paste: Boolean);var  TargetPath: string;  RenameOnCollision: Boolean;begin  TargetPath := '';  RenameOnCollision := False;  if Assigned(Item) then  begin    if Assigned(Item.Data) then    begin      if ItemIsParentDirectory(Item) then        TargetPath := ExcludeTrailingPathDelimiter(ExtractFilePath(Path))      else        TargetPath := IncludeTrailingPathDelimiter(PathName) + ItemFileName(Item);    end;  end    else  begin    TargetPath := PathName;    RenameOnCollision := DDOwnerIsSource and (Effect = DROPEFFECT_COPY);  end;  if TargetPath <> '' then    PerformDragDropFileOperation(TargetPath, Effect, RenameOnCollision, Paste);end;procedure TDirView.ReLoad(CacheIcons: Boolean);begin  if not FLoadEnabled then FDirty := True    else inherited;end; {ReLoad}function TDirView.GetAttrString(Attr: Integer): string;const  Attrs: array[1..5] of Integer =    (FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_ARCHIVE,     FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_HIDDEN,     FILE_ATTRIBUTE_READONLY);  AttrChars: array[1..5] of Char = ('c', 'a', 's', 'h', 'r');var  Index: Integer;  LowBound: Integer;begin  Result := '';  if Attr <> 0 then  begin    LowBound := Low(Attrs);    for Index := LowBound to High(Attrs) do      if (Attr and Attrs[Index] <> 0) then        Result := Result + AttrChars[Index]      else        Result := Result;  end;end; {GetAttrString}function TDirView.GetFileInfo(  CanUsePIDL: Boolean; PIDL: PItemIDList; Path: string; CanTimeout: Boolean;  dwFileAttributes: DWORD; var psfi: TSHFileInfoW; uFlags: UINT): DWORD_PTR;var  pszPath: LPCWSTR;  cbFileInfo: UINT;begin  cbFileInfo := SizeOf(psfi);  FillChar(psfi, cbFileInfo, #0);  if CanUsePIDL and Assigned(PIDL) then  begin    pszPath := PChar(PIDL);    uFlags := uFlags or SHGFI_PIDL;  end    else pszPath := PChar(Path);  // CanTimeout is False in scenarios, where we did not have any reports of hangs, to avoid thread overhead.  if TimeoutShellIconRetrieval and CanTimeout then  begin    Result := SHGetFileInfoWithTimeout(pszPath, dwFileAttributes, psfi, cbFileInfo, uFlags, MSecsPerSec div 4);  end    else  begin    Result := SHGetFileInfo(pszPath, dwFileAttributes, psfi, cbFileInfo, uFlags);  end;  if Result = 0 then  begin    psfi.szTypeName[0] := #0;    psfi.iIcon := 0;  end;end;procedure TDirView.DoFetchIcon(  FilePath: string; IsSpecialExt: Boolean; CanTimeout: Boolean; FileRec: PFileRec; var ImageIndex: Integer; var TypeName: string);var  Eaten: ULONG;  shAttr: ULONG;  FileInfo: TShFileInfo;  ForceByName: Boolean;  FileIconForName: string;begin  {Fetch the Item FQ-PIDL:}  if not Assigned(FileRec^.PIDL) and IsSpecialExt then  begin    try      ShAttr := 0;      FDesktopFolder.ParseDisplayName(        ParentForm.Handle, nil, PChar(FilePath), Eaten, FileRec^.PIDL, ShAttr);    except    end;  end;  if FileRec^.IsDirectory then  begin    if FDriveType = DRIVE_FIXED then    begin      try        {Retrieve icon and typename for the directory}        GetFileInfo(True, FileRec^.PIDL, FilePath, False, 0, FileInfo, SHGFI_TYPENAME or SHGFI_SYSICONINDEX);        if (FileInfo.iIcon <= 0) or (FileInfo.iIcon > SmallImages.Count) then        begin          {Invalid icon returned: retry with access file attribute flag:}          GetFileInfo(            False, nil, FilePath, False,            FILE_ATTRIBUTE_DIRECTORY, FileInfo, SHGFI_TYPENAME or SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES);        end;        TypeName := FileInfo.szTypeName;        ImageIndex := FileInfo.iIcon;      except        {Capture exceptions generated by the shell}        TypeName := StdDirTypeName;        ImageIndex := StdDirIcon;      end;    end      else    begin      TypeName := StdDirTypeName;      ImageIndex := StdDirIcon;    end;  end    else  begin    {Retrieve icon and typename for the file}    try      ForceByName := False;      FileIconForName := FilePath;      if Assigned(OnFileIconForName) then      begin        OnFileIconForName(Self, FileIconForName);        ForceByName := (FileIconForName <> FilePath);      end;      // Files with PIDL are typically .exe files.      // It may take long to retrieve an icon from exe file.      // We typically do not get here, now that we have UseIconUpdateThread enabled.      if GetFileInfo(           (not ForceByName), FileRec^.PIDL, FileIconForName, CanTimeout, FILE_ATTRIBUTE_NORMAL, FileInfo,           SHGFI_TYPENAME or SHGFI_USEFILEATTRIBUTES or SHGFI_SYSICONINDEX) = 0 then      begin        if Assigned(FileRec^.PIDL) then        begin          FileInfo.iIcon := DefaultExeIcon;        end;      end;      TypeName := FileInfo.szTypeName;      ImageIndex := FileInfo.iIcon;    except      {Capture exceptions generated by the shell}      ImageIndex := UnknownFileIcon;    end;  end;end;procedure TDirView.GetDisplayData(Item: TListItem; FetchIcon: Boolean);var  FileInfo: TShFileInfo;  IsSpecialExt: Boolean;  FileRec: PFileRec;  FilePath: string;  FileAttributes: UINT;begin  Assert(Assigned(Item) and Assigned(Item.Data));  FileRec := PFileRec(Item.Data);  with FileRec^ do  begin    IsSpecialExt := MatchesFileExt(FileExt, SpecialExtensions);    FetchIcon := IconEmpty and (FetchIcon or not IsSpecialExt);    if Empty or FetchIcon then    begin      FilePath := FPath + '\' + FileName;      if FetchIcon then      begin        DoFetchIcon(FilePath, IsSpecialExt, True, FileRec, ImageIndex, TypeName);        IconEmpty := False;        if Length(TypeName) = 0 then          TypeName := Format(STextFileExt, [FileExt]);      end        else      begin        try          if IsDirectory then FileAttributes := FILE_ATTRIBUTE_DIRECTORY            else FileAttributes := FILE_ATTRIBUTE_NORMAL;          GetFileInfo(            False, nil, FilePath, False, FileAttributes, FileInfo,            SHGFI_TYPENAME or SHGFI_USEFILEATTRIBUTES);          TypeName := FileInfo.szTypeName;        except          {Capture exceptions generated by the shell}          TypeName := '';        end;        if IconEmpty then        begin          if FileExt = ExeExtension then ImageIndex := DefaultExeIcon            else ImageIndex := UnknownFileIcon;        end;      end;      Empty := False;    end;  end;end; {GetDisplayData}function TDirView.GetDirOK: Boolean;begin  Result := FDirOK;end;function TDirView.ItemFullFileName(Item: TListItem): string;begin  if Assigned(Item) and Assigned(Item.Data) then  begin    if not IsRecycleBin then    begin      if PFileRec(Item.Data)^.IsParentDir then      begin        Result := ExcludeTrailingBackslash(ExtractFilePath(FPath));      end        else      begin        Result := FPath + '\' + PFileRec(Item.Data)^.FileName;      end;    end      else    Result := PFileRec(Item.Data)^.FileName;  end    else  Result := EmptyStr;end; {ItemFullFileName}function TDirView.ItemFileNameOnly(Item: TListItem): string;begin  Assert(Assigned(Item) and Assigned(Item.Data));  Result := PFileRec(Item.Data)^.FileName;  SetLength(Result, Length(Result) - Length(ItemFileExt(Item)));end; {ItemFileNameOnly}function TDirView.ItemFileExt(Item: TListItem): string;begin  Assert(Assigned(Item) and Assigned(Item.Data));  Result := ExtractFileExt(PFileRec(Item.Data)^.FileName);end; {ItemFileExt}function CompareFileType(I1, I2: TListItem; P1, P2: PFileRec): Integer;var  Key1, Key2: string;begin  if P1.Empty then TDirView(I1.ListView).GetDisplayData(I1, False);  if P2.Empty then TDirView(I2.ListView).GetDisplayData(I2, False);  if P1.IsDirectory then  begin    Key1 := P1.TypeName + ' ' + P1.DisplayName;    Key2 := P2.TypeName + ' ' + P2.DisplayName;  end    else  begin    Key1 := P1.TypeName + ' ' + P1.FileExt + ' ' + P1.DisplayName;    Key2 := P2.TypeName + ' ' + P2.FileExt + ' ' + P2.DisplayName;  end;  Result := CompareLogicalTextPas(Key1, Key2, TDirView(I1.ListView).NaturalOrderNumericalSorting);end;function CompareFileTime(P1, P2: PFileRec): Integer;var  Time1, Time2: Int64;begin  Time1 := Int64(P1.FileTime.dwHighDateTime) shl 32 + P1.FileTime.dwLowDateTime;  Time2 := Int64(P2.FileTime.dwHighDateTime) shl 32 + P2.FileTime.dwLowDateTime;  if Time1 < Time2 then Result := -1    else  if Time1 > Time2 then Result := 1    else Result := 0; // fallbackend;function GetItemFileSize(P: PFileRec): Int64; inline;begin  Result := 0;  if P.Size >= 0 then Result := P.Size    else  if P.CalculatedSize >= 0 then Result := P.CalculatedSize;end;function CompareFile(I1, I2: TListItem; AOwner: TDirView): Integer; stdcall;var  ConsiderDirection: Boolean;  P1, P2: PFileRec;begin  ConsiderDirection := True;  if I1 = I2 then Result := 0    else  if I1 = nil then Result := -1    else  if I2 = nil then Result := 1    else  begin    P1 := PFileRec(I1.Data);    P2 := PFileRec(I2.Data);    if P1.isParentDir then    begin      Result := -1;      ConsiderDirection := False;    end      else    if P2.isParentDir then    begin      Result := 1;      ConsiderDirection := False;    end      else    {Directories should always appear "grouped":}    if P1.isDirectory <> P2.isDirectory then    begin      if P1.isDirectory then      begin        Result := -1;        ConsiderDirection := False;      end        else      begin        Result := 1;        ConsiderDirection := False;      end;    end      else    begin      Result := 0;      if P1.isDirectory and AOwner.AlwaysSortDirectoriesByName then      begin        // fallback      end        else      begin        case AOwner.DirColProperties.SortDirColumn of          dvName:            ; // fallback          dvSize:            if GetItemFileSize(P1) < GetItemFileSize(P2) then Result := -1              else            if GetItemFileSize(P1) > GetItemFileSize(P2) then Result := 1              else ; // fallback          dvType:            Result := CompareFileType(I1, I2, P1, P2);          dvChanged:            Result := CompareFileTime(P1, P2);          dvAttr:            if P1.Attr < P2.Attr then Result := -1              else            if P1.Attr > P2.Attr then Result := 1              else ; // fallback          dvExt:            if not P1.isDirectory then            begin              Result := CompareLogicalTextPas(                P1.FileExt + ' ' + P1.DisplayName, P2.FileExt + ' ' + P2.DisplayName,                AOwner.NaturalOrderNumericalSorting);            end              else ; //fallback          else            ; // fallback        end;      end;      if Result = 0 then      begin        Result := CompareLogicalTextPas(P1.DisplayName, P2.DisplayName, AOwner.NaturalOrderNumericalSorting)      end;    end;  end;  if ConsiderDirection and (not AOwner.SortAscending) then  begin    Result := -Result;  end;end;procedure TDirView.SortItems;begin  if HandleAllocated then  begin    StopIconUpdateThread;    try      CustomSortItems(@CompareFile);    finally      if (not Loading) then        StartIconUpdateThread;    end;  endend;procedure TDirView.ValidateFile(Item : TListItem);var  Index: Integer;begin  if Assigned(Item) and Assigned(Item.Data) then  begin    Index := Item.Index;    if not FileExists(ApiPath(ItemFullFileName(Items[Index]))) then    begin      Item.Delete;    end;  end;end; {ValidateFile}procedure TDirView.ValidateFile(FileName: TFileName);var  FilePath: string;begin  FilePath := ExcludeTrailingPathDelimiter(ExtractFilePath(FileName));  if IsRecycleBin then ValidateFile(FindFileItem(FileName))    else  if FilePath = Path then    ValidateFile(FindFileItem(ExtractFileName(FileName)));end; {ValidateFile}procedure TDirView.ValidateSelectedFiles;var  FileList: TStrings;  i: Integer;  ToDelete: Boolean;  Updating: Boolean;  Updated: Boolean;  Item: TListItem;begin  if SelCount > 50 then Reload2    else  begin    Updating := False;    Updated := False;    FileList := CustomCreateFileList(True, False, True, nil, True);    try      for i := 0 to FileList.Count - 1 do      begin        Item := TListItem(FileList.Objects[i]);        if ItemIsDirectory(Item) then          ToDelete := not DirectoryExists(ApiPath(FileList[i]))        else          ToDelete := not FileExists(ApiPath(FileList[i]));        if ToDelete then        begin          if (SelCount > 10) and (not Updating) then          begin            Items.BeginUpdate;            Updating := True;          end;          with PFileRec(Item.Data)^ do          begin            Dec(FFilesSize, Size);            // No need to decrease FFilesSelSize here as LVIF_STATE/deselect            // is called for item being deleted          end;          Item.Delete;          Updated := True;        end;      end;    finally      if Updating then        Items.EndUpdate;      if Updated then        DoUpdateStatusBar;      FileList.Free;    end;  end;end; {ValidateSelectedFiles}procedure TDirView.CreateDirectory(DirName: string);var  SRec: SysUtils.TSearchRec;  Item: TListItem;begin  // keep absolute path as is  if ExtractFileDrive(DirName) = '' then    DirName := Path + '\' + DirName;  if WatchForChanges then StopWatchThread;  if Assigned(FDriveView) then    TDriveView(FDriveView).StopWatchThread;  StopIconUpdateThread;  try    {create the physical directory:}    Win32Check(Windows.CreateDirectory(PChar(ApiPath(DirName)), nil));    if IncludeTrailingBackslash(ExtractFilePath(ExpandFileName(DirName))) =         IncludeTrailingBackslash(Path) then    begin      {Create the TListItem:}      if FindFirst(ApiPath(DirName), faAnyFile, SRec) = 0 then      begin        Item := AddItem(SRec);        ItemFocused := FindFileItem(GetFileRec(Item.Index)^.FileName);        SortItems;        if Assigned(ItemFocused) then        begin          ItemFocused.MakeVisible(False);        end;      end;      FindClose(SRec);    end;  finally    StartIconUpdateThread;    if WatchForChanges then StartWatchThread;    if Assigned(DriveView) then      with DriveView do      begin        if Assigned(Selected) then          ValidateDirectory(Selected);        TDriveView(FDriveView).StartWatchThread;      end;  end;end; {CreateDirectory}procedure TDirView.DisplayContextMenu(Where: TPoint);var  FileList: TStringList;  Index: Integer;  Item: TListItem;  DefDir: string;  Verb: string;  PIDLArray: PPIDLArray;  Count: Integer;  DiffSelectedPath: Boolean;  WithEdit: Boolean;  PIDLRel: PItemIDList;  PIDLPath: PItemIDList;  Handled: Boolean;begin  GetDir(0, DefDir);  ChDir(PathName);  Verb := EmptyStr;  StopWatchThread;  try    try      if Assigned(OnContextPopup) then      begin        Handled := False;        OnContextPopup(Self, ScreenToClient(Where), Handled);        if Handled then Abort;      end;      if (MarkedCount > 1) and         ((not Assigned(ItemFocused)) or ItemFocused.Selected) then      begin        if FIsRecycleBin then        begin          Count := 0;          GetMem(PIDLArray, SizeOf(PItemIDList) * SelCount);          try            FillChar(PIDLArray^, Sizeof(PItemIDList) * SelCount, #0);            for Index := Selected.Index to Items.Count - 1 do              if Items[Index].Selected then              begin                PIDL_GetRelative(PFileRec(Items[Index].Data)^.PIDL, PIDLPath, PIDLRel);                FreePIDL(PIDLPath);                PIDLArray^[Count] := PIDLRel;                Inc(Count);              end;            try              ShellDisplayContextMenu(ParentForm.Handle, Where, iRecycleFolder, Count,                PidlArray^[0], False, Verb, False);            finally              for Index := 0 to Count - 1 do                FreePIDL(PIDLArray[Index]);            end;          finally            FreeMem(PIDLArray, Count);          end;        end          else        begin          FileList := TStringList.Create;          CreateFileList(False, True, FileList);          for Index := 0 to FileList.Count - 1 do            FileList[Index] := ExtractFileName(FileList[Index]);          ShellDisplayContextMenu(ParentForm.Handle, Where, PathName,            FileList, Verb, False);          FileList.Destroy;        end;        {------------ Cut -----------}        if Verb = shcCut then        begin          LastClipBoardOperation := cboCut;          {Clear items previous marked as cut:}          Item := GetNextItem(nil, sdAll, [isCut]);          while Assigned(Item) do          begin            Item.Cut := False;            Item := GetNextItem(Item, sdAll, [isCut]);          end;          {Set property cut to TRUE for all selected items:}          Item := GetNextItem(nil, sdAll, [isSelected]);          while Assigned(Item) do          begin            Item.Cut := True;            Item := GetNextItem(Item, sdAll, [isSelected]);          end;        end          else        {----------- Copy -----------}        if Verb = shcCopy then LastClipBoardOperation := cboCopy          else        {----------- Paste ----------}        if Verb = shcPaste then            PasteFromClipBoard(ItemFullFileName(Selected))          else        if not FIsRecycleBin then Reload2;      end        else      if Assigned(ItemFocused) and Assigned(ItemFocused.Data) then      begin        Verb := EmptyStr;        WithEdit := not FIsRecycleBin and CanEdit(ItemFocused);        LoadEnabled := True;        if FIsRecycleBin then        begin          PIDL_GetRelative(PFileRec(ItemFocused.Data)^.PIDL, PIDLPath, PIDLRel);          ShellDisplayContextMenu(ParentForm.Handle, Where,            iRecycleFolder, 1, PIDLRel, False, Verb, False);          FreePIDL(PIDLRel);          FreePIDL(PIDLPath);        end          else        begin          ShellDisplayContextMenu(ParentForm.Handle, Where,            ItemFullFileName(ItemFocused), WithEdit, Verb,            not PFileRec(ItemFocused.Data)^.isDirectory);          LoadEnabled := True;        end; {not FIsRecycleBin}        {---------- Rename ----------}        if Verb = shcRename then ItemFocused.EditCaption          else        {------------ Cut -----------}        if Verb = shcCut then        begin          LastClipBoardOperation := cboCut;          Item := GetNextItem(nil, sdAll, [isCut]);          while Assigned(Item) do          begin            Item.Cut := False;            Item := GetNextItem(ITem, sdAll, [isCut]);          end;          ItemFocused.Cut := True;        end          else        {----------- Copy -----------}        if Verb = shcCopy then LastClipBoardOperation := cboCopy          else        {----------- Paste ----------}        if Verb = shcPaste then        begin          if PFileRec(ItemFocused.Data)^.IsDirectory then          PasteFromClipBoard(ItemFullFileName(ItemFocused));        end          else        if not FIsRecycleBin then Reload2;      end;    finally      ChDir(DefDir);    end;    if IsRecycleBin and (Verb <> shcCut) and (Verb <> shcProperties) and (SelCount > 0) then    begin      DiffSelectedPath := False;      for Index := Selected.Index to Items.Count - 1 do        if ExtractFilePath(PFileRec(Items[Index].Data)^.FileName) <> FPath + '\' then        begin          DiffSelectedPath := True;          Break;        end;      if DiffSelectedPath then      begin        StartFileDeleteThread;        Exit;      end;    end;    Sleep(250);    ValidateSelectedFiles;  finally    StartWatchThread;  end;end;procedure TDirView.GetDisplayInfo(ListItem: TListItem;  var DispInfo: TLVItem);var  Value: string;  ASize: Int64;  FetchIcon: Boolean;begin  Assert(Assigned(ListItem) and Assigned(ListItem.Data));  with PFileRec(ListItem.Data)^, DispInfo  do  begin    {Fetch display data of current file:}    if Empty then    begin      FetchIcon :=        IconEmpty and        (not FUseIconUpdateThread or (ViewStyle <> vsReport));      GetDisplayData(ListItem, FetchIcon);    end;    if IconEmpty and       (not FUseIconUpdateThread or        (ViewStyle <> vsReport)) and       ((DispInfo.Mask and LVIF_IMAGE) <> 0) then      GetDisplayData(ListItem, True);    {Set IconUpdatethread :}    if IconEmpty and UseIconUpdateThread and (not FIsRecycleBin) then    begin      IconUpdateEnqueue(ListItem);    end;    if (DispInfo.Mask and LVIF_TEXT) <> 0 then    begin      Value := '';      if iSubItem = 0 then Value := DisplayName        else      if iSubItem < DirViewColumns then      begin        case TDirViewCol(iSubItem) of          dvSize: {Size:     }            begin              if not IsDirectory then ASize := Size                else ASize := CalculatedSize;              if ASize >= 0 then Value := FormatPanelBytes(ASize, FormatSizeBytes);            end;          dvType: {FileType: }            Value := TypeName;          dvChanged: {Date}            // Keep consistent with UserModificationStr            Value := FormatDateTime('ddddd tt', FileTimeToDateTime(FileTime));          dvAttr: {Attrs:}            Value := GetAttrString(Attr);          dvExt:            Value := FileExt;        end {Case}      end; {SubItem}      StrPLCopy(pszText, Value, cchTextMax - 1);    end;    {Set display icon of current file:}    if (iSubItem = 0) and ((DispInfo.Mask and LVIF_IMAGE) <> 0) then    begin      iImage := PFileRec(ListItem.Data).ImageIndex;      Mask := Mask or LVIF_DI_SETITEM;    end;  end; {With PFileRec Do}end;function TDirView.ItemColor(Item: TListItem): TColor;begin  if PFileRec(Item.Data).Attr and FILE_ATTRIBUTE_COMPRESSED <> 0 then  begin    if SupportsDarkMode and DarkMode then Result := clSkyBlue      else Result := clBlue;  end    else  if DimmHiddenFiles and not Item.Selected and     (PFileRec(Item.Data).Attr and FILE_ATTRIBUTE_HIDDEN <> 0) then      Result := clGrayText    else  Result := clDefaultItemColor;end;function TDirView.ItemThumbnail(Item: TListItem; Size: TSize): TBitmap;var  ItemData: PFileRec;begin  if not UseIconUpdateThread then  begin    // not supported without update thread    Result := nil;  end    else  begin    ItemData := PFileRec(Item.Data);    if (not Assigned(ItemData.Thumbnail)) or       (ItemData.ThumbnailSize <> Size) then    begin      FreeAndNil(ItemData.Thumbnail);      ItemData.ThumbnailSize := Size;      Result := nil;      IconUpdateEnqueue(Item);    end      else    begin      Result := ItemData.Thumbnail;    end;  end;end;procedure TDirView.StartFileDeleteThread;var  Files: TStringList;begin  Files := TStringList.Create;  try    CreateFileList(False, True, Files);    TFileDeleteThread.Create(Files, MaxWaitTimeOut, SignalFileDelete);  finally    Files.Free;  end;end;procedure TDirView.IconUpdateEnqueue(ListItem: TListItem);var  Schedule: TIconUpdateSchedule;begin  if not FIconUpdateSet.ContainsKey(ListItem.Index) then  begin    FIconUpdateSet.Add(ListItem.Index, False);    Schedule.Index := ListItem.Index;    FIconUpdateQueue.Enqueue(Schedule);    Assert(FIconUpdateSet.Count = FIconUpdateQueue.Count + FIconUpdateQueueDeferred.Count);  end;  StartIconUpdateThread;end;function TDirView.IconUpdatePeek: Integer;begin  if FIconUpdateQueue.Count > 0 then    Result := FIconUpdateQueue.Peek.Index  else if FIconUpdateQueueDeferred.Count > 0 then    Result := FIconUpdateQueueDeferred.Peek.Index  else    Result := -1;end;procedure TDirView.IconUpdateDequeue;begin  if FIconUpdateQueue.Count > 0 then    FIconUpdateQueue.Dequeue  else    FIconUpdateQueueDeferred.Dequeue;  FIconUpdateSet.Remove(FIconUpdateThread.FCurrentIndex);  Assert(FIconUpdateSet.Count = FIconUpdateQueue.Count + FIconUpdateQueueDeferred.Count);end;function TDirView.ThumbnailNeeded(ItemData: PFileRec): Boolean;begin  Result := (not Assigned(ItemData.Thumbnail)) and (ItemData.ThumbnailSize <> ThumbnailNotNeeded);end;procedure TDirView.DoFetchIconUpdate;var  Item: TListItem;  ItemData: PFileRec;  Invisible: Boolean;begin  FIconUpdateThread.FCurrentIndex := IconUpdatePeek;  if FIconUpdateThread.FCurrentIndex < 0 then  begin    FIconUpdateThread.Suspend;  end    else  begin    // We could loop here until we find a valid item, but we want to release the GUI thread frequently    if FIconUpdateThread.FCurrentIndex >= Items.Count then    begin      FIconUpdateThread.FCurrentIndex := -1;      IconUpdateDequeue;    end      else    begin      Item := Items[FIconUpdateThread.FCurrentIndex];      Assert(Assigned(Item));      Assert(Assigned(Item.Data));      ItemData := PFileRec(Item.Data);      Invisible := not IsItemVisible(Item);      if (Invisible or (not FThumbnail)) and         (not Assigned(ItemData.Thumbnail)) then      begin        ItemData.ThumbnailSize := ThumbnailNotNeeded; // not needed anymore      end;      // Deprioritizing unvisible item taken from priority queue.      // As we ask Window to cache the image index (LVIF_DI_SETITEM), we won't get asked again,      // so we have to retrieve the image eventually (but not thumbnail)      if (FIconUpdateQueue.Count > 0) and         Invisible then      begin        Assert(not ThumbnailNeeded(ItemData));        if ItemData.IconEmpty then        begin          FIconUpdateQueueDeferred.Enqueue(FIconUpdateQueue.Dequeue);        end          else        begin          IconUpdateDequeue;        end;        FIconUpdateThread.FCurrentIndex := -1;      end        else      begin        if ItemData.IconEmpty or           ThumbnailNeeded(ItemData) then        begin          FIconUpdateThread.FCurrentFilePath := ItemFullFileName(Item);          FIconUpdateThread.FCurrentItemData := ItemData^;        end          else        begin          IconUpdateDequeue;          FIconUpdateThread.FCurrentIndex := -1;        end;      end;    end;  end;end;procedure TDirView.DoUpdateIcon;var  Item: TListItem;  ItemData: PFileRec;  LVI: TLVItem;begin  // otherwise just throw away the resolved icon and let this or future background thread  // retry with the same or different item the next time  if (not Loading) and     (not FIconUpdateThread.Terminated) and     (FIconUpdateThread.FCurrentIndex = IconUpdatePeek) then  begin    IconUpdateDequeue;    if (FIconUpdateThread.FCurrentIndex >= 0) and       (FIconUpdateThread.FCurrentIndex < Items.Count) then    begin      Item := Items[FIconUpdateThread.FCurrentIndex];      if FIconUpdateThread.FCurrentFilePath = ItemFullFileName(Item) then      begin        Assert(Assigned(Item));        Assert(Assigned(Item.Data));        ItemData := PFileRec(Item.Data);        if (FIconUpdateThread.FSyncIcon >= 0) and (ItemData^.ImageIndex <> FIconUpdateThread.FSyncIcon) then        begin          ItemData^.ImageIndex := FIconUpdateThread.FSyncIcon;          if not FIconUpdateThread.Terminated then          begin            {To avoid flickering of the display use Listview_SetItem            instead of using the property ImageIndex:}            LVI.mask := LVIF_IMAGE;            LVI.iItem := FIconUpdateThread.FCurrentIndex;            LVI.iSubItem := 0;            LVI.iImage := I_IMAGECALLBACK;            ListView_SetItem(Handle, LVI);          end;        end;        ItemData^.IconEmpty := False;        if Assigned(FIconUpdateThread.FSyncThumbnail) then        begin          ItemData^.Thumbnail := FIconUpdateThread.FSyncThumbnail;          FIconUpdateThread.FSyncThumbnail := nil;          // It can happen that this is called while the very item is being painted,          // (particularly ImageFactory.GetImage pumps the queue).          // Calling InvalidateRect directly would get ignored then.          PostMessage(WindowHandle, WM_USER_INVALIDATEITEM, Item.Index, 0);        end;      end;    end;  end;end; {TIconUpdateThread.DoUpdateIcon}procedure TDirView.StartIconUpdateThread;begin  if DirOK and UseIconUpdateThread then  begin    if not Assigned(FIconUpdateThread) then      FIconUpdateThread := TIconUpdateThread.Create(Self);    FIconUpdateThread.Resume;  end;end; {StartIconUpdateThread}procedure TDirView.StopIconUpdateThread;begin  if Assigned(FIconUpdateThread) then  begin    FIconUpdateThread.Terminate;    FIconUpdateThread.Priority := tpHigher;    if FIconUpdateThread.Suspended then      FIconUpdateThread.Resume;    if not FIconUpdateThread.WaitFor(MSecsPerSec div 4) then    begin      // This prevents Destroy from waiting for (stalled) thread      FIconUpdateThread.Suspend;    end;    FreeAndNil(FIconUpdateThread);  end;end; {StopIconUpdateThread}procedure TDirView.StopWatchThread;begin  if Assigned(FDiscMonitor) then  begin    FDiscMonitor.Enabled := False;  end;end; {StopWatchThread}procedure TDirView.StartWatchThread;begin  if (Length(Path) > 0) and WatchForChanges and DirOK then  begin    if not Assigned(FDiscMonitor) then    begin      FDiscMonitor := TDiscMonitor.Create(Self);      with FDiscMonitor do      begin        ChangeDelay := msThreadChangeDelay;        SubTree := False;        Filters := [moDirName, moFileName, moSize, moAttributes, moLastWrite];        SetDirectory(PathName);        OnChange := ChangeDetected;        OnInvalid := ChangeInvalid;        Open;      end;    end      else    begin      FDiscMonitor.SetDirectory(PathName);      FDiscMonitor.Enabled := True;    end;  end;end; {StartWatchThread}procedure TDirView.TimerOnTimer(Sender: TObject);begin  if not Loading then  begin    // fix by MP: disable timer and reload directory before call to event    FChangeTimer.Enabled := False;    FChangeTimer.Interval := 0;    Reload2;  end;end; {TimerOnTimer}procedure TDirView.ChangeDetected(Sender: TObject; const Directory: string;  var SubdirsChanged: Boolean);begin  // avoid prolonging the actual update with each change, as if continous change  // is occuring in current directory, the panel will never be updated  if not FChangeTimer.Enabled then  begin    FDirty := True;    FChangeTimer.Interval := FChangeInterval;    FChangeTimer.Enabled := True;  end;end; {ChangeDetected}procedure TDirView.ChangeInvalid(Sender: TObject; const Directory: string;  const ErrorStr: string);begin  FDiscMonitor.Close;end; {ChangeInvalid}function TDirView.WatchThreadActive: Boolean;begin  Result := WatchForChanges and Assigned(FDiscMonitor) and    FDiscMonitor.Active and FDiscMonitor.Enabled;end; {WatchThreadActive}procedure TDirView.SetChangeInterval(Value: Cardinal);begin  if Value > 0 then  begin    FChangeInterval := Value;    FChangeTimer.Interval := Value;  end;end; {SetChangeInterval}procedure TDirView.SetDirColProperties(Value: TDirViewColProperties);begin  if Value <> ColProperties then    ColProperties := Value;end;function TDirView.GetDirColProperties: TDirViewColProperties;begin  Result := TDirViewColProperties(ColProperties);end;procedure TDirView.SetWatchForChanges(Value: Boolean);begin  if WatchForChanges <> Value then  begin    FWatchForChanges := Value;    if not (csDesigning in ComponentState) then    begin      if Value then StartWatchThread        else StopWatchThread;    end;  end;end; {SetWatchForChanges}procedure TDirView.DisplayPropertiesMenu;var  FileList: TStringList;  Index: Integer;  PIDLRel: PItemIDList;  PIDLPath: PItemIDList;begin  if not Assigned(ItemFocused) then      ShellExecuteContextCommand(ParentForm.Handle, shcProperties, PathName)    else  if (not IsRecycleBin) and (MarkedCount > 1) and ItemFocused.Selected then  begin    FileList := TStringList.Create;    try      CreateFileList(False, True, FileList);      for Index := 0 to Pred(FileList.Count) do        FileList[Index] := ExtractFileName(FileList[Index]);      ShellExecuteContextCommand(ParentForm.Handle, shcProperties,        PathName, FileList);    finally      FileList.Free;    end;  end    else  if Assigned(ItemFocused.Data) then  begin    if IsRecycleBin then    begin      if Assigned(PFileRec(ItemFocused.Data)^.PIDL) then      begin        PIDL_GetRelative(PFileRec(ItemFocused.Data)^.PIDL, PIDLPath, PIDLRel);        ShellExecuteContextCommand(ParentForm.Handle, shcProperties, iRecycleFolder, 1, PIDLRel);        FreePIDL(PIDLRel);        FreePIDL(PIDLPath);      end;    end      else    ShellExecuteContextCommand(ParentForm.Handle, shcProperties,      ItemFullFileName(ItemFocused));  end;end;procedure TDirView.ExecuteFile(Item: TListItem);var  DefDir: string;  FileName: string;begin  if (UpperCase(PFileRec(Item.Data)^.FileExt) = 'LNK') or     PFileRec(Item.Data)^.IsDirectory then  begin    if PFileRec(Item.Data)^.IsDirectory then    begin      FileName := ItemFullFileName(Item);      if not DirectoryExistsFix(FileName) then      begin        Reload2;        if Assigned(FDriveView) and Assigned(FDriveView.Selected) then          with FDriveView do            ValidateDirectory(Selected);        Exit;      end;    end      else    FileName := ResolveFileShortCut(ItemFullFileName(Item), True);    if DirectoryExistsFix(FileName) then    begin      Path := FileName;      Exit;    end      else    if not FileExistsFix(ApiPath(FileName)) then    begin      Exit;    end;  end;  GetDir(0, DefDir);  ChDir(PathName);  try    ShellExecuteContextCommand(ParentForm.Handle, shcDefault,      ItemFullFileName(Item));  finally    ChDir(DefDir);  end;end;procedure TDirView.ExecuteDrive(Drive: string);var  APath: string;  DriveRoot: string;begin  if Assigned(FLastPath) and FLastPath.ContainsKey(Drive) then  begin    APath := FLastPath[Drive];    if not DirectoryExists(ApiPath(APath)) then    begin      if DriveInfo.IsRealDrive(Drive) then        APath := Format('%s:', [Drive])      else        APath := Drive;    end;  end    else  begin    if DriveInfo.IsRealDrive(Drive) then    begin      GetDir(Integer(Drive[1]) - Integer('A') + 1, APath);      DriveRoot := DriveInfo.GetDriveRoot(Drive);      // When the drive is not valid, the GetDir returns the current drive working directory, detect that,      // and let it fail later when trying to open root of the invalid drive.      // (maybe GetLongPathName would not have that side effect?)      if not StartsText(DriveRoot, APath) then        APath := DriveRoot;      APath := ExcludeTrailingPathDelimiter(APath);    end      else    begin      APath := Drive;    end;  end;  if Path <> APath then    Path := APath;end;procedure TDirView.ExecuteHomeDirectory;begin  Path := HomeDirectory;end;procedure TDirView.ExecuteParentDirectory;begin  if Valid then  begin    if Assigned(DriveView) and Assigned(DriveView.Selected) then    begin      DriveView.Selected := DriveView.Selected.Parent    end      else    begin      Path := ExtractFilePath(Path);    end;  end;end;procedure TDirView.ExecuteRootDirectory;begin  if Valid then  begin    FNotRelative := True;    try      Path := ExtractFileDrive(Path);    finally      FNotRelative := False;    end;  end;end;procedure TDirView.Delete(Item: TListItem);begin  if Assigned(Item) and Assigned(Item.Data) and not (csRecreating in ControlState) then    with PFileRec(Item.Data)^ do    begin      SetLength(FileName, 0);      SetLength(TypeName, 0);      SetLength(DisplayName, 0);      if Assigned(PIDL) then FreePIDL(PIDL);      FreeAndNil(Thumbnail);      Dispose(PFileRec(Item.Data));      Item.Data := nil;    end;  inherited Delete(Item);end; {Delete}procedure TDirView.InternalEdit(const HItem: TLVItem);var  Item: TListItem;  Info: string;  NewCaption: string;  IsDirectory: Boolean;begin  Item := GetItemFromHItem(HItem);  IsDirectory := DirectoryExists(ItemFullFileName(Item));  NewCaption := HItem.pszText;  StopWatchThread;  if IsDirectory and Assigned(FDriveView) then    TDriveView(FDriveView).StopWatchThread;  with FFileOperator do  begin    Flags := FileOperatorDefaultFlags + [foNoConfirmation];    Operation := foRename;    OperandFrom.Clear;    OperandTo.Clear;    OperandFrom.Add(ItemFullFileName(Item));    OperandTo.Add(FPath + '\' + HItem.pszText);  end;  try    if FFileOperator.Execute then    begin      if IsDirectory and Assigned(FDriveView) then        with FDriveView do          if Assigned(Selected) then            ValidateDirectory(Selected);      with GetFileRec(Item.Index)^ do      begin        Empty := True;        IconEmpty := True;        FileName := NewCaption;        DisplayName := FileName;        FileExt := UpperCase(ExtractFileExt(HItem.pszText));        FileExt := Copy(FileExt, 2, Length(FileExt) - 1);        TypeName := EmptyStr;        if Assigned(PIDL) then          FreePIDL(PIDL);      end;      GetDisplayData(Item, True);      ResetItemImage(Item.Index);      UpdateItems(Item.Index, Item.Index);      if Assigned(OnEdited) then OnEdited(Self, Item, NewCaption);      if Item <> nil then Item.Caption := NewCaption;      SortItems;      if Assigned(ItemFocused) then ItemFocused.MakeVisible(False);    end      else    begin      Item.Caption := GetFileRec(Item.Index)^.FileName;      Item.Update;      if FileOrDirExists(IncludeTrailingPathDelimiter(FPath) + HItem.pszText) then        Info := SErrorRenameFileExists + HItem.pszText      else        Info := SErrorRenameFile + HItem.pszText;      MessageBeep(MB_ICONHAND);      if MessageDlg(FormatLastOSError(Info), mtError, [mbOK, mbAbort], 0) = mrOK then        RetryRename(HItem.pszText);    end;  finally    Sleep(0);    LoadEnabled := True;    if FWatchForChanges and (not WatchThreadActive) then      StartWatchThread;    if Assigned(FDriveView) then      TDriveView(FDriveView).StartWatchThread;  end;end;function TDirView.ItemFileName(Item: TListItem): string;begin  if Assigned(Item) and Assigned(Item.Data) then    Result := ExtractFileName(PFileRec(Item.Data)^.FileName)  else    Result := '';end;function TDirView.ItemFileSize(Item: TListItem): Int64;begin  Result := 0;  if Assigned(Item) and Assigned(Item.Data) then    Result := GetItemFileSize(PFileRec(Item.Data));end;function TDirView.ItemFileTime(Item: TListItem;  var Precision: TDateTimePrecision): TDateTime;begin  Result := FileTimeToDateTime(PFileRec(Item.Data)^.FileTime);  Precision := tpMillisecond;end;function TDirView.ItemImageIndex(Item: TListItem;  Cache: Boolean): Integer;begin  if Assigned(Item) and Assigned(Item.Data) then  begin    if PFileRec(Item.Data)^.IconEmpty then    begin      if Cache then Result := -1        else Result := UnknownFileIcon;    end      else    begin      if (not Cache) or MatchesFileExt(PFileRec(Item.Data)^.FileExt, SpecialExtensions) then        Result := PFileRec(Item.Data)^.ImageIndex      else        Result := -1    end;  end    else Result := -1;end;procedure TDirView.Notification(AComponent: TComponent; Operation: TOperation);begin  inherited Notification(AComponent, Operation);  if (Operation = opRemove) and (AComponent = FDriveView) then    FDriveView := nil;end; {Notification}procedure TDirView.ReloadDirectory;begin  Reload(True);end;procedure TDirView.ResetItemImage(Index: Integer);var  LVI: TLVItem;begin  with PFileRec(Items[Index].Data)^, LVI do  begin    {Update imageindex:}    Mask := LVIF_STATE or LVIF_DI_SETITEM or LVIF_IMAGE;    iItem := Index;    iSubItem := 0;    if ListView_GetItem(Handle, LVI) then    begin      iImage := I_IMAGECALLBACK;      Mask := Mask and (not LVIF_DI_SETITEM);      ListView_SetItem(Handle, LVI);    end;  end; {With}end; {ResetItemImage}{ Drag&Drop handling }procedure TDirView.SignalFileDelete(Sender: TObject; Files: TStringList);{Called by TFileDeleteThread, when a file was deleted by the Drag&Drop target window:}var  Index: Integer;begin  if Files.Count > 0 then    for Index := 0 to Files.Count - 1 do      ValidateFile(Files[Index]);end;procedure TDirView.DDMenuPopup(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;  AMinCustCmd: Integer; grfKeyState: Longint; pt: TPoint);begin  if Assigned(FDriveView) then  begin    // When a change is detected while menu is popped up    // it loses focus (or something similar)    // preventing it from handling subsequent click.    // This typically happens when right-dragging from remote to local panel,    // what causes temp directory being created+deleted.    // This is HACK, we should implement some uniform watch disabling/enabling    TDriveView(FDriveView).SuspendChangeTimer;  end;  inherited;end;procedure TDirView.DDMenuDone(Sender: TObject; AMenu: HMenu);begin  if not WatchThreadActive then  begin    FChangeTimer.Interval := Min(FChangeInterval * 2, 3000);    FChangeTimer.Enabled  := True;  end;  if Assigned(FDriveView) then  begin    TDriveView(FDriveView).ResumeChangeTimer;  end;  inherited;end;procedure TDirView.DDDropHandlerSucceeded(Sender: TObject; grfKeyState: Longint;  Point: TPoint; dwEffect: Longint);begin  // Not sure why is this here. There's no "disable" counterparty.  if not WatchThreadActive then  begin    FChangeTimer.Interval := FChangeInterval;    FChangeTimer.Enabled  := True;  end;  inherited;end;procedure TDirView.AddToDragFileList(FileList: TFileList; Item: TListItem);begin  Assert(Assigned(Item));  if IsRecycleBin then  begin    if Assigned(Item.Data) then    begin      if UpperCase(ExtractFileExt(PFileRec(Item.Data)^.DisplayName)) =        ('.' + PFileRec(Item.Data)^.FileExt) then          FileList.AddItemEx(PFileRec(Item.Data)^.PIDL,            ItemFullFileName(Item), PFileRec(Item.Data)^.DisplayName)      else        FileList.AddItemEx(PFileRec(Item.Data)^.PIDL,          ItemFullFileName(Item), PFileRec(Item.Data)^.DisplayName +            ExtractFileExt(PFileRec(Item.Data)^.FileName));    end;  end    else inherited;end;procedure TDirView.DDDragDetect(grfKeyState: Longint; DetectStart, Point: TPoint;  DragStatus: TDragDetectStatus);var  WasWatchThreadActive: Boolean;begin  if (DragStatus = ddsDrag) and (MarkedCount > 0) then  begin    WasWatchThreadActive := WatchThreadActive;    inherited;    if (LastDDResult = drMove) and (not WasWatchThreadActive) then      StartFileDeleteThread;  end;end; {DDDragDetect}procedure TDirView.DDChooseEffect(grfKeyState: Integer; var dwEffect: Integer; PreferredEffect: Integer);begin  if DragDropFilesEx.OwnerIsSource and     (dwEffect = DROPEFFECT_COPY) and (not Assigned(DropTarget)) then  begin    dwEffect := DROPEFFECT_NONE  end    else  if (grfKeyState and (MK_CONTROL or MK_SHIFT) = 0) and (PreferredEffect = 0) then  begin    if FDragDrive <> '' then    begin      if ExeDrag and DriveInfo.IsFixedDrive(DriveInfo.GetDriveKey(Path)) and DriveInfo.IsFixedDrive(FDragDrive) then      begin        dwEffect := DROPEFFECT_LINK;      end        else      begin        if DragOnDriveIsMove and           (not DDOwnerIsSource or Assigned(DropTarget)) and           ((SameText(FDragDrive, DriveInfo.GetDriveKey(Path)) and (dwEffect = DROPEFFECT_COPY) and           (DragDropFilesEx.AvailableDropEffects and DROPEFFECT_MOVE <> 0))             or IsRecycleBin) then        begin          dwEffect := DROPEFFECT_MOVE;        end;      end;    end;  end;  inherited;end;procedure TDirView.PerformDragDropFileOperation(TargetPath: string;  Effect: Integer; RenameOnCollision: Boolean; Paste: Boolean);var  Index: Integer;  SourcePath: string;  OldCursor: TCursor;  OldWatchForChanges: Boolean;  IsRecycleBin: Boolean;  SourceIsDirectory: Boolean;  Node: TTreeNode;begin  if DragDropFilesEx.FileList.Count > 0 then  begin    if not DirectoryExists(TargetPath) then    begin      Reload(True);      DDError(DDPathNotFoundError);    end      else    begin      IsRecycleBin := Self.IsRecycleBin or        ((DropTarget <> nil) and ItemIsRecycleBin(DropTarget));      if not (DragDropFilesEx.FileNamesAreMapped and IsRecycleBin) then      begin        OldCursor := Screen.Cursor;        OldWatchForChanges := WatchForChanges;        SourceIsDirectory := True;        SourcePath := EmptyStr;        try          Screen.Cursor := crHourGlass;          WatchForChanges := False;          if Effect in [DROPEFFECT_COPY, DROPEFFECT_MOVE] then          begin            StopWatchThread;            if Assigned(DriveView) then              TDriveView(DriveView).StopWatchThread;            if (DropSourceControl <> Self) and               (DropSourceControl is TDirView) then                TDirView(DropSourceControl).StopWatchThread;            if DropFiles(                 DragDropFilesEx, Effect, FFileOperator, TargetPath, RenameOnCollision, IsRecycleBin,                 ConfirmDelete, ConfirmOverwrite, Paste,                 Self, OnDDFileOperation, SourcePath, SourceIsDirectory) then            begin              ReLoad2;              if Assigned(OnDDFileOperationExecuted) then                OnDDFileOperationExecuted(Self, Effect, SourcePath, TargetPath);            end;          end            else          if Effect = DROPEFFECT_LINK then          (* Create Link requested: *)          begin            StopWatchThread;            for Index := 0 to DragDropFilesEx.FileList.Count - 1 do            begin              if not DropLink(PFDDListItem(DragDropFilesEx.FileList[Index]), TargetPath) then              begin                DDError(DDCreateShortCutError);              end;            end;            ReLoad2;          end;          if Assigned(DropSourceControl) and             (DropSourceControl is TDirView) and             (DropSourceControl <> Self) and             (Effect = DROPEFFECT_MOVE) then          begin            TDirView(DropSourceControl).ValidateSelectedFiles;          end;          if Assigned(FDriveView) and SourceIsDirectory then          begin            with TDriveView(FDriveView) do            begin              try                ValidateDirectory(FindNodeToPath(TargetPath));              except              end;              if (Effect = DROPEFFECT_MOVE) or IsRecycleBin then              try                Node := TryFindNodeToPath(SourcePath);                // If the path is not even in the tree, do not bother.                // This is particularly for dragging from remote folder, when the source path in %TEMP% and                // calling ValidateDirectory would load whole TEMP (and typically also "C:\Users")                if Assigned(Node) then                begin                  if Assigned(Node.Parent) then                    Node := Node.Parent;                  ValidateDirectory(Node);                end;              except              end;            end;          end;        finally          FFileOperator.OperandFrom.Clear;          FFileOperator.OperandTo.Clear;          if Assigned(FDriveView) then            TDriveView(FDriveView).StartWatchThread;          Sleep(0);          WatchForChanges := OldWatchForChanges;          if (DropSourceControl <> Self) and (DropSourceControl is TDirView) then            TDirView(DropSourceControl).StartWatchThread;          Screen.Cursor := OldCursor;        end;      end;    end;  end;end; {PerformDragDropFileOperation}procedure TDirView.DDError(ErrorNo: TDDError);begin  if Assigned(OnDDError) then OnDDError(Self, ErrorNo)    else  raise EDragDrop.Create(Format(SDragDropError, [Ord(ErrorNo)]));end; {DDError}function TDirView.GetCanUndoCopyMove: Boolean;begin  Result := Assigned(FFileOperator) and FFileOperator.CanUndo;end; {CanUndoCopyMove}function TDirView.UndoCopyMove : Boolean;var  LastTarget: string;  LastSource: string;begin  Result := False;  if FFileOperator.CanUndo then  begin    Lasttarget := FFileOperator.LastOperandTo[0];    LastSource := FFileOperator.LastOperandFrom[0];    if Assigned(FDriveView) then      TDriveView(FDriveView).StopAllWatchThreads;    Result := FFileOperator.UndoExecute;    if not WatchthreadActive then      Reload2;    if Assigned(FDriveView) then      with TDriveView(FDriveView) do      begin        ValidateDirectory(FindNodeToPath(ExtractFilePath(LastTarget)));        ValidateDirectory(FindNodeToPath(ExtractFilePath(LastSource)));        StartAllWatchThreads;      end;  end;end; {UndoCopyMove}procedure TDirView.EmptyClipboard;var  Item: TListItem;begin  if Windows.OpenClipBoard(0) then  begin    Windows.EmptyClipBoard;    Windows.CloseClipBoard;    if LastClipBoardOperation <> cboNone then    begin      Item := GetNextItem(nil, sdAll, [isCut]);      while Assigned(Item) do      begin        Item.Cut := False;        Item := GetNextItem(Item, sdAll, [isCut]);      end;    end;    LastClipBoardOperation := cboNone;    if Assigned(FDriveView) then      TDriveView(FDriveView).LastPathCut := '';  end;end; {EmptyClipBoard}function TDirView.DoCopyToClipboard(Focused: Boolean; Cut: Boolean; Operation: TClipBoardOperation): Boolean;var  Item: TListItem;  SaveCursor: TCursor;begin  SaveCursor := Screen.Cursor;  Screen.Cursor := crHourGlass;  try    Result := False;    EmptyClipBoard;    DragDropFilesEx.FileList.Clear;    if OperateOnFocusedFile(Focused) or (SelCount > 0) then    begin      if OperateOnFocusedFile(Focused) then      begin        DragDropFilesEx.FileList.AddItem(nil, ItemFullFileName(ItemFocused));      end        else      begin        Item := GetNextItem(nil, sdAll, [isSelected]);        while Assigned(Item) do        begin          DragDropFilesEx.FileList.AddItem(nil, ItemFullFileName(Item));          Item.Cut := Cut;          Item := GetNextItem(Item, sdAll, [isSelected]);        end;      end;      Result := DragDropFilesEx.CopyToClipBoard;      LastClipBoardOperation := Operation;    end;  finally    Screen.Cursor := SaveCursor;  end;end; {DoCopyToClipBoard}function TDirView.CopyToClipBoard(Focused: Boolean): Boolean;begin  Result := DoCopyToClipboard(Focused, False, cboCopy);end;function TDirView.CutToClipBoard(Focused: Boolean): Boolean;begin  Result := DoCopyToClipboard(Focused, True, cboCut);end;function TDirView.PasteFromClipBoard(TargetPath: string): Boolean;begin  DragDropFilesEx.FileList.Clear;  Result := False;  if CanPasteFromClipBoard and {MP}DragDropFilesEx.GetFromClipBoard{/MP}    then  begin    if TargetPath = '' then      TargetPath := PathName;    case LastClipBoardOperation of      cboNone:        begin          PerformDragDropFileOperation(TargetPath, DROPEFFECT_COPY, False, True);          if Assigned(OnDDExecuted) then OnDDExecuted(Self, DROPEFFECT_COPY);        end;      cboCopy:        begin          PerformDragDropFileOperation(TargetPath, DROPEFFECT_COPY,            ExcludeTrailingPathDelimiter(ExtractFilePath(TFDDListItem(DragDropFilesEx.FileList[0]^).Name)) = Path, True);          if Assigned(OnDDExecuted) then OnDDExecuted(Self, DROPEFFECT_COPY);        end;      cboCut:        begin          PerformDragDropFileOperation(TargetPath, DROPEFFECT_MOVE, False, True);          if Assigned(OnDDExecuted) then OnDDExecuted(Self, DROPEFFECT_MOVE);          EmptyClipBoard;        end;    end;    Result := True;  end;end; {PasteFromClipBoard}function TDirView.DragCompleteFileList: Boolean;begin  Result := inherited DragCompleteFileList and    (FDriveType <> DRIVE_REMOVABLE);end;function TDirView.DuplicateSelectedFiles: Boolean;begin  Result := False;  if SelCount > 0 then  begin    Result := CopyToClipBoard(False);    if Result then      try        SelectNewFiles := True;        Selected := nil;        Result := PasteFromClipBoard();      finally        SelectNewFiles := False;        if Assigned(Selected) then        begin          ItemFocused := Selected;          Selected.MakeVisible(False);          if SelCount = 1 then            Selected.EditCaption;        end;      end;  end;  EmptyClipBoard;end; {DuplicateFiles}function TDirView.NewColProperties: TCustomListViewColProperties;begin  Result := TDirViewColProperties.Create(Self);end;function TDirView.SortAscendingByDefault(Index: Integer): Boolean;begin  Result := not (TDirViewCol(Index) in [dvSize, dvChanged]);end;procedure TDirView.SetItemImageIndex(Item: TListItem; Index: Integer);begin  Assert(Assigned(Item));  if Assigned(Item.Data) then    with PFileRec(Item.Data)^ do    begin      ImageIndex := Index;      IconEmpty := (ImageIndex < 0);    end;end;procedure TDirView.SetItemCalculatedSize(Item: TListItem; ASize: Int64);var  OldSize: Int64;begin  Assert(Assigned(Item) and Assigned(Item.Data));  with PFileRec(Item.Data)^ do  begin    OldSize := CalculatedSize;    CalculatedSize := ASize;  end;  ItemCalculatedSizeUpdated(Item, OldSize, ASize);end;{=================================================================}initialization  LastClipBoardOperation := cboNone;  DaylightHack := (not IsWin7);end.
 |