CustomDirView.pas 101 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311
  1. unit CustomDirView;
  2. interface
  3. {$R DirImg.res}
  4. {$WARN UNIT_PLATFORM OFF}
  5. {$WARN SYMBOL_PLATFORM OFF}
  6. uses
  7. Windows, Messages, Classes, Graphics, Controls,
  8. Forms, ComCtrls, ShellAPI, ComObj, ShlObj, Dialogs,
  9. ActiveX, CommCtrl, Extctrls, ImgList, Menus, FileCtrl,
  10. PIDL, BaseUtils, DragDrop, DragDropFilesEx, IEDriveInfo,
  11. IEListView, PathLabel, SysUtils, PasTools;
  12. const
  13. clDefaultItemColor = -(COLOR_ENDCOLORS + 1);
  14. WM_USER_RENAME = WM_USER + 57;
  15. oiNoOverlay = $00;
  16. oiDirUp = $01;
  17. oiLink = $02;
  18. oiBrokenLink = $04;
  19. oiPartial = $08;
  20. oiEncrypted = $10;
  21. DefaultHistoryCount = 200;
  22. const
  23. DDDragStartDelay = 500000;
  24. DirAttrMask = SysUtils.faDirectory or SysUtils.faSysFile or SysUtils.faHidden;
  25. const
  26. _XBUTTON1 = $0001;
  27. _XBUTTON2 = $0002;
  28. type
  29. TStatusFileInfo = record
  30. FilesCount: Integer;
  31. SelectedCount: Integer;
  32. FilesSize: Int64;
  33. SelectedSize: Int64;
  34. HiddenCount: Integer;
  35. FilteredCount: Integer;
  36. end;
  37. type
  38. {Drag&Drop events:}
  39. TDDError = (DDCreateShortCutError, DDPathNotFoundError);
  40. TDDOnDragEnter = procedure(Sender: TObject; DataObj: IDataObject; grfKeyState: Longint; Point: TPoint; var dwEffect: Longint; var Accept: Boolean) of object;
  41. TDDOnDragLeave = procedure(Sender: TObject) of object;
  42. TDDOnDragOver = procedure(Sender: TObject; grfKeyState: Longint; Point: TPoint; var dwEffect: Longint) of object;
  43. TDDOnDrop = procedure(Sender: TObject; DataObj: IDataObject; grfKeyState: Longint; Point: TPoint; var dwEffect: Longint) of object;
  44. TDDOnQueryContinueDrag = procedure(Sender: TObject; FEscapePressed: BOOL; grfKeyState: Longint; var Result: HResult) of object;
  45. TDDOnGiveFeedback = procedure(Sender: TObject; dwEffect: Longint; var Result: HResult) of object;
  46. TDDOnChooseEffect = procedure(Sender: TObject; grfKeyState: Longint; var dwEffect: Longint) of object;
  47. TDDOnDragDetect = procedure(Sender: TObject; grfKeyState: Longint; DetectStart, Point: TPoint; DragStatus: TDragDetectStatus) of object;
  48. TDDOnCreateDragFileList = procedure(Sender: TObject; FileList: TFileList; var Created: Boolean) of object;
  49. TDDOnCreateDataObject = procedure(Sender: TObject; var DataObject: TDataObject) of object;
  50. TDDOnTargetHasDropHandler = procedure(Sender: TObject; Item: TListItem; var Effect: Integer; var DropHandler: Boolean) of object;
  51. TOnProcessDropped = procedure(Sender: TObject; grfKeyState: Longint; Point: TPoint; var dwEffect: Longint) of object;
  52. TDDErrorEvent = procedure(Sender: TObject; ErrorNo: TDDError) of object;
  53. TDDExecutedEvent = procedure(Sender: TObject; dwEffect: Longint) of object;
  54. TDDFileOperationEvent = procedure(Sender: TObject; dwEffect: LongInt; SourcePath, TargetPath: string;
  55. var DoOperation: Boolean) of object;
  56. TDDFileOperationExecutedEvent = procedure(Sender: TObject; dwEffect: LongInt; SourcePath, TargetPath: string) of object;
  57. TDirViewExecFileEvent = procedure(Sender: TObject; Item: TListItem; var AllowExec: Boolean) of object;
  58. TMatchMaskEvent = procedure(Sender: TObject; FileName: string; Directory: Boolean; Size: Int64; Modification: TDateTime; Masks: string; var Matches: Boolean; AllowImplicitMatches: Boolean) of object;
  59. TDirViewGetOverlayEvent = procedure(Sender: TObject; Item: TListItem; var Indexes: Word) of object;
  60. TDirViewUpdateStatusBarEvent = procedure(Sender: TObject; const FileInfo: TStatusFileInfo) of object;
  61. TDirViewBusy = procedure(Sender: TObject; Busy: Integer; var State: Boolean) of object;
  62. TBusyOperation = reference to procedure;
  63. type
  64. TCustomDirView = class;
  65. TSelAttr = (selDontCare, selYes, selNo);
  66. TFileFilter = record
  67. Masks: string;
  68. Directories: Boolean;
  69. end;
  70. TDirViewNotifyEvent = procedure(Sender: TCustomDirView) of object;
  71. TDVGetFilterEvent = procedure(Sender: TCustomDirView; Select: Boolean;
  72. var Filter: TFileFilter) of object;
  73. TDVHistoryGoEvent = procedure(Sender: TCustomDirView; Index: Integer; var Cancel: Boolean) of object;
  74. TCompareCriteria = (ccTime, ccSize);
  75. TCompareCriterias = set of TCompareCriteria;
  76. TWMXMouse = packed record
  77. Msg: Cardinal;
  78. Keys: Word;
  79. Button: Word;
  80. Pos: TSmallPoint;
  81. Result: Longint
  82. end;
  83. TCustomizableDragDropFilesEx = class(TDragDropFilesEx)
  84. public
  85. function Execute(DataObject: TDataObject): TDragResult;
  86. end;
  87. TCustomDirView = class(TCustomIEListView)
  88. private
  89. FAddParentDir: Boolean;
  90. FDimmHiddenFiles: Boolean;
  91. FFormatSizeBytes: TFormatBytesStyle;
  92. FWantUseDragImages: Boolean;
  93. FDragDropFilesEx: TCustomizableDragDropFilesEx;
  94. FUseSystemContextMenu: Boolean;
  95. FOnStartLoading: TNotifyEvent;
  96. FOnLoaded: TNotifyEvent;
  97. FExeDrag: Boolean;
  98. FDDLinkOnExeDrag: Boolean;
  99. FOnDDDragEnter: TDDOnDragEnter;
  100. FOnDDDragLeave: TDDOnDragLeave;
  101. FOnDDDragOver: TDDOnDragOver;
  102. FOnDDDrop: TDDOnDrop;
  103. FOnDDQueryContinueDrag: TDDOnQueryContinueDrag;
  104. FOnDDGiveFeedback: TDDOnGiveFeedback;
  105. FOnDDChooseEffect: TDDOnChooseEffect;
  106. FOnDDDragDetect: TDDOnDragDetect;
  107. FOnDDCreateDragFileList: TDDOnCreateDragFileList;
  108. FOnDDProcessDropped: TOnProcessDropped;
  109. FOnDDError: TDDErrorEvent;
  110. FOnDDExecuted: TDDExecutedEvent;
  111. FOnDDFileOperation: TDDFileOperationEvent;
  112. FOnDDFileOperationExecuted: TDDFileOperationExecutedEvent;
  113. FOnDDEnd: TNotifyEvent;
  114. FOnDDCreateDataObject: TDDOnCreateDataObject;
  115. FOnDDTargetHasDropHandler: TDDOnTargetHasDropHandler;
  116. FOnDDMenuPopup: TOnMenuPopup;
  117. FOnExecFile: TDirViewExecFileEvent;
  118. FForceRename: Boolean;
  119. FLastDDResult: TDragResult;
  120. FLastRenameName: string;
  121. FContextMenu: Boolean;
  122. FDragEnabled: Boolean;
  123. FDragPos: TPoint;
  124. FStartPos: TPoint;
  125. FDDOwnerIsSource: Boolean;
  126. FAbortLoading: Boolean;
  127. FAnimation: TAnimate;
  128. FBackCount: Integer;
  129. FDontRecordPath: Boolean;
  130. FDragOnDriveIsMove: Boolean;
  131. FNotifyEnabled: Boolean;
  132. FDragStartTime: TFileTime;
  133. FHistoryPaths: TStrings;
  134. FImageList16: TImageList;
  135. FImageList32: TImageList;
  136. FLoadAnimation: Boolean;
  137. FMaxHistoryCount: Integer;
  138. FPathLabel: TCustomPathLabel;
  139. FOnUpdateStatusBar: TDirViewUpdateStatusBarEvent;
  140. FOnHistoryChange: TDirViewNotifyEvent;
  141. FOnHistoryGo: TDVHistoryGoEvent;
  142. FOnPathChange: TDirViewNotifyEvent;
  143. FShowHiddenFiles: Boolean;
  144. FSavedSelection: Boolean;
  145. FSavedSelectionFile: string;
  146. FSavedSelectionLastFile: string;
  147. FSavedNames: TStringList;
  148. FPendingFocusSomething: Boolean;
  149. FOnMatchMask: TMatchMaskEvent;
  150. FOnGetOverlay: TDirViewGetOverlayEvent;
  151. FMask: string;
  152. FNaturalOrderNumericalSorting: Boolean;
  153. FScrollOnDragOver: TListViewScrollOnDragOver;
  154. FStatusFileInfo: TStatusFileInfo;
  155. FDoubleBufferedScrollingWorkaround: Boolean;
  156. FOnBusy: TDirViewBusy;
  157. procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
  158. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  159. procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;
  160. procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
  161. procedure WMContextMenu(var Message: TWMContextMenu); message WM_CONTEXTMENU;
  162. procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  163. procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;
  164. procedure WMXButtonUp(var Message: TWMXMouse); message WM_XBUTTONUP;
  165. procedure WMAppCommand(var Message: TMessage); message WM_APPCOMMAND;
  166. procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
  167. procedure LVMSetExtendedListViewStyle(var Message: TMessage); message LVM_SETEXTENDEDLISTVIEWSTYLE;
  168. procedure CMRecreateWnd(var Message: TMessage); message CM_RECREATEWND;
  169. procedure CMDPIChanged(var Message: TMessage); message CM_DPICHANGED;
  170. procedure DumbCustomDrawItem(Sender: TCustomListView; Item: TListItem;
  171. State: TCustomDrawState; var DefaultDraw: Boolean);
  172. procedure DumbCustomDrawSubItem(Sender: TCustomListView;
  173. Item: TListItem; SubItem: Integer; State: TCustomDrawState;
  174. var DefaultDraw: Boolean);
  175. function GetFilesMarkedSize: Int64;
  176. function GetForwardCount: Integer;
  177. function GetHistoryPath(Index: Integer): string;
  178. function GetSelectedNamesSaved: Boolean;
  179. function GetTargetPopupMenu: Boolean;
  180. function GetUseDragImages: Boolean;
  181. procedure SetMaxHistoryCount(Value: Integer);
  182. procedure SetPathLabel(Value: TCustomPathLabel);
  183. procedure SetTargetPopupMenu(Value: Boolean);
  184. procedure WMUserRename(var Message: TMessage); message WM_User_Rename;
  185. protected
  186. FCaseSensitive: Boolean;
  187. FDirty: Boolean;
  188. FFilesSize: Int64;
  189. FFilesSelSize: Int64;
  190. FHasParentDir: Boolean;
  191. FIsRecycleBin: Boolean;
  192. FLastPath: string;
  193. FHistoryPath: string;
  194. FLoadEnabled: Boolean;
  195. FLoading: Boolean;
  196. FSelectFile: string;
  197. FWatchForChanges: Boolean;
  198. FInvalidNameChars: string;
  199. FDragDrive: string;
  200. procedure AddToDragFileList(FileList: TFileList; Item: TListItem); virtual;
  201. function CanEdit(Item: TListItem): Boolean; override;
  202. function CanChangeSelection(Item: TListItem; Select: Boolean): Boolean; override;
  203. procedure CancelEdit;
  204. procedure ClearItems; override;
  205. function GetDirOK: Boolean; virtual; abstract;
  206. procedure DDDragDetect(grfKeyState: Longint; DetectStart, Point: TPoint; DragStatus: TDragDetectStatus); virtual;
  207. procedure DDDragEnter(DataObj: IDataObject; grfKeyState: Longint; Point: TPoint; var dwEffect: longint; var Accept: Boolean);
  208. procedure DDDragLeave;
  209. procedure DDDragOver(grfKeyState: Longint; Point: TPoint; var dwEffect: Longint);
  210. procedure DDChooseEffect(grfKeyState: Integer; var dwEffect: Integer); virtual;
  211. procedure DDDrop(DataObj: IDataObject; grfKeyState: LongInt; Point: TPoint; var dwEffect: Longint);
  212. procedure DDDropHandlerSucceeded(Sender: TObject; grfKeyState: Longint; Point: TPoint; dwEffect: Longint); virtual;
  213. procedure DDGiveFeedback(dwEffect: Longint; var Result: HResult); virtual;
  214. procedure DDMenuPopup(Sender: TObject; AMenu: HMenu; DataObj: IDataObject;
  215. AMinCustCmd:integer; grfKeyState: Longint; pt: TPoint); virtual;
  216. procedure DDMenuDone(Sender: TObject; AMenu: HMenu); virtual;
  217. procedure DDProcessDropped(Sender: TObject; grfKeyState: Longint;
  218. Point: TPoint; dwEffect: Longint);
  219. procedure DDQueryContinueDrag(FEscapePressed: LongBool;
  220. grfKeyState: Longint; var Result: HResult); virtual;
  221. procedure DDSpecifyDropTarget(Sender: TObject; DragDropHandler: Boolean;
  222. Point: TPoint; var pidlFQ : PItemIDList; var Filename: string); virtual;
  223. procedure GetDisplayInfo(ListItem: TListItem; var DispInfo: TLVItem); virtual;
  224. function GetDragSourceEffects: TDropEffectSet; virtual;
  225. function GetPathName: string; virtual; abstract;
  226. function GetFilesCount: Integer; virtual;
  227. procedure ColClick(Column: TListColumn); override;
  228. procedure CreateWnd; override;
  229. procedure DestroyWnd; override;
  230. function CustomCreateFileList(Focused, OnlyFocused: Boolean;
  231. FullPath: Boolean; FileList: TStrings = nil; ItemObject: Boolean = False): TStrings;
  232. function CustomDrawItem(Item: TListItem; State: TCustomDrawState;
  233. Stage: TCustomDrawStage): Boolean; override;
  234. function CustomDrawSubItem(Item: TListItem; SubItem: Integer;
  235. State: TCustomDrawState; Stage: TCustomDrawStage): Boolean; override;
  236. procedure CustomSortItems(SortProc: Pointer);
  237. procedure Delete(Item: TListItem); override;
  238. procedure DoAnimation(Start: Boolean);
  239. procedure DoHistoryChange; dynamic;
  240. function DragCompleteFileList: Boolean; virtual;
  241. procedure Edit(const HItem: TLVItem); override;
  242. procedure EndSelectionUpdate; override;
  243. procedure Execute(Item: TListItem); virtual;
  244. procedure ExecuteFile(Item: TListItem); virtual; abstract;
  245. procedure FocusSomething; override;
  246. function GetIsRoot: Boolean; virtual; abstract;
  247. function ItemCanDrag(Item: TListItem): Boolean; virtual;
  248. function ItemColor(Item: TListItem): TColor; virtual;
  249. function ItemImageIndex(Item: TListItem; Cache: Boolean): Integer; virtual; abstract;
  250. // ItemIsDirectory and ItemFullFileName is in public block
  251. function ItemIsRecycleBin(Item: TListItem): Boolean; virtual;
  252. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  253. procedure KeyPress(var Key: Char); override;
  254. procedure KeyUp(var Key: Word; Shift: TShiftState); override;
  255. procedure LoadFiles; virtual; abstract;
  256. procedure PerformItemDragDropOperation(Item: TListItem; Effect: Integer); virtual; abstract;
  257. procedure ProcessChangedFiles(DirView: TCustomDirView;
  258. FileList: TStrings; FullPath: Boolean; ExistingOnly: Boolean;
  259. Criterias: TCompareCriterias);
  260. procedure ReloadForce(CacheIcons : Boolean);
  261. procedure RetryRename(NewName: string);
  262. procedure SetAddParentDir(Value: Boolean); virtual;
  263. procedure SetDimmHiddenFiles(Value: Boolean); virtual;
  264. procedure SetItemImageIndex(Item: TListItem; Index: Integer); virtual; abstract;
  265. procedure SetLoadEnabled(Enabled : Boolean); virtual;
  266. procedure SetMultiSelect(Value: Boolean); override;
  267. function GetPath: string; virtual; abstract;
  268. function GetValid: Boolean; override;
  269. procedure InternalEdit(const HItem: TLVItem); virtual; abstract;
  270. function ItemIsFile(Item: TListItem): Boolean; virtual; abstract;
  271. function ItemMatchesFilter(Item: TListItem; const Filter: TFileFilter): Boolean; virtual; abstract;
  272. function ItemOverlayIndexes(Item: TListItem): Word; virtual;
  273. procedure LimitHistorySize;
  274. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  275. procedure PathChanged; virtual;
  276. procedure PathChanging(Relative: Boolean);
  277. procedure SetPath(Value: string); virtual; abstract;
  278. procedure SetShowHiddenFiles(Value: Boolean); virtual;
  279. procedure SetFormatSizeBytes(Value: TFormatBytesStyle);
  280. procedure SetViewStyle(Value: TViewStyle); override;
  281. procedure SetWatchForChanges(Value: Boolean); virtual;
  282. function TargetHasDropHandler(Item: TListItem; Effect: Integer): Boolean; virtual;
  283. procedure UpdatePathLabel; dynamic;
  284. procedure UpdatePathLabelCaption; dynamic;
  285. procedure UpdateStatusBar; dynamic;
  286. function FileNameMatchesMasks(FileName: string; Directory: Boolean; Size: Int64; Modification: TDateTime; Masks: string; AllowImplicitMatches: Boolean): Boolean;
  287. function EnableDragOnClick: Boolean; override;
  288. procedure SetMask(Value: string); virtual;
  289. procedure SetNaturalOrderNumericalSorting(Value: Boolean);
  290. procedure ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
  291. procedure ScrollOnDragOverAfterUpdate;
  292. procedure DoHistoryGo(Index: Integer);
  293. procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  294. procedure WMKillFocus(var Message: TWMKillFocus); message WM_KILLFOCUS;
  295. procedure EnsureSelectionRedrawn;
  296. function HiddenCount: Integer; virtual; abstract;
  297. function FilteredCount: Integer; virtual; abstract;
  298. function DoBusy(Busy: Integer): Boolean;
  299. function StartBusy: Boolean;
  300. procedure EndBusy;
  301. function IsBusy: Boolean;
  302. procedure BusyOperation(Operation: TBusyOperation);
  303. procedure DoDisplayPropertiesMenu;
  304. procedure DoExecute(Item: TListItem);
  305. procedure DoExecuteParentDirectory;
  306. procedure Load(DoFocusSomething: Boolean); virtual;
  307. procedure NeedImageLists(Recreate: Boolean);
  308. procedure FreeImageLists;
  309. property ImageList16: TImageList read FImageList16;
  310. property ImageList32: TImageList read FImageList32;
  311. public
  312. constructor Create(AOwner: TComponent); override;
  313. destructor Destroy; override;
  314. procedure Reload(CacheIcons: Boolean); virtual;
  315. function CreateFocusedFileList(FullPath: Boolean; FileList: TStrings = nil): TStrings;
  316. function CreateFileList(Focused: Boolean; FullPath: Boolean; FileList: TStrings = nil): TStrings;
  317. function AnyFileSelected(OnlyFocused: Boolean; FilesOnly: Boolean;
  318. FocusedFileOnlyWhenFocused: Boolean): Boolean;
  319. procedure SelectFiles(Filter: TFileFilter; Select: Boolean);
  320. procedure ExecuteHomeDirectory; virtual; abstract;
  321. procedure ExecuteParentDirectory; virtual; abstract;
  322. procedure ExecuteRootDirectory; virtual; abstract;
  323. procedure ExecuteCurrentFile();
  324. procedure CreateDirectory(DirName: string); virtual; abstract;
  325. function FindFileItem(FileName: string): TListItem;
  326. procedure HistoryGo(Index: Integer);
  327. function ItemIsDirectory(Item: TListItem): Boolean; virtual; abstract;
  328. function ItemIsParentDirectory(Item: TListItem): Boolean; virtual; abstract;
  329. function ItemFullFileName(Item: TListItem): string; virtual; abstract;
  330. function ItemFileName(Item: TListItem): string; virtual; abstract;
  331. function ItemFileSize(Item: TListItem): Int64; virtual; abstract;
  332. function ItemFileTime(Item: TListItem; var Precision: TDateTimePrecision): TDateTime; virtual; abstract;
  333. procedure ReloadDirectory; virtual; abstract;
  334. procedure DisplayPropertiesMenu; virtual; abstract;
  335. function CreateChangedFileList(DirView: TCustomDirView; FullPath: Boolean;
  336. ExistingOnly: Boolean; Criterias: TCompareCriterias): TStrings;
  337. procedure CompareFiles(DirView: TCustomDirView; ExistingOnly: Boolean;
  338. Criterias: TCompareCriterias); virtual;
  339. procedure SaveSelection;
  340. procedure RestoreSelection;
  341. procedure DiscardSavedSelection;
  342. procedure SaveSelectedNames;
  343. procedure RestoreSelectedNames;
  344. procedure ContinueSession(Continue: Boolean);
  345. function CanPasteFromClipBoard: Boolean; dynamic;
  346. function PasteFromClipBoard(TargetPath: string = ''): Boolean; virtual; abstract;
  347. function SaveState: TObject;
  348. procedure RestoreState(AState: TObject);
  349. procedure ClearState;
  350. procedure DisplayContextMenu(Where: TPoint); virtual; abstract;
  351. procedure DisplayContextMenuInSitu;
  352. property AddParentDir: Boolean read FAddParentDir write SetAddParentDir default False;
  353. property DimmHiddenFiles: Boolean read FDimmHiddenFiles write SetDimmHiddenFiles default True;
  354. property DragDropFilesEx: TCustomizableDragDropFilesEx read FDragDropFilesEx;
  355. property FormatSizeBytes: TFormatBytesStyle read FFormatSizeBytes write SetFormatSizeBytes default fbNone;
  356. property WantUseDragImages: Boolean read FWantUseDragImages write FWantUseDragImages default False;
  357. property UseDragImages: Boolean read GetUseDragImages stored False;
  358. property FullDrag default True;
  359. property TargetPopupMenu: Boolean read GetTargetPopupMenu write SetTargetPopupMenu default True;
  360. property DDOwnerIsSource: Boolean read FDDOwnerIsSource;
  361. property FilesSize: Int64 read FFilesSize;
  362. property FilesSelSize: Int64 read FFilesSelSize;
  363. property FilesCount: Integer read GetFilesCount;
  364. property FilesMarkedSize: Int64 read GetFilesMarkedSize;
  365. property HasParentDir: Boolean read FHasParentDir;
  366. property Path: string read GetPath write SetPath;
  367. property PathName: string read GetPathName;
  368. property UseSystemContextMenu: Boolean read FUseSystemContextMenu
  369. write FUseSystemContextMenu default True;
  370. property Loading: Boolean read FLoading;
  371. property AbortLoading: Boolean read FAbortLoading write FAbortLoading stored False;
  372. property BackCount: Integer read FBackCount;
  373. {Enable or disable populating the item list:}
  374. property LoadAnimation: Boolean read FLoadAnimation write FLoadAnimation default True;
  375. property LoadEnabled: Boolean read FLoadEnabled write SetLoadEnabled default True;
  376. {Displayed data is not valid => reload required}
  377. property Dirty: Boolean read FDirty;
  378. property DirOK: Boolean read GetDirOK;
  379. property LastPath: string read FLastPath;
  380. property IsRecycleBin: Boolean read FIsRecycleBin;
  381. property DDLinkOnExeDrag: Boolean read FDDLinkOnExeDrag
  382. write FDDLinkOnExeDrag default False;
  383. property DragOnDriveIsMove: Boolean read FDragOnDriveIsMove write FDragOnDriveIsMove;
  384. property DragSourceEffects: TDropEffectSet read GetDragSourceEffects{ write FDragSourceEffects};
  385. property ExeDrag: Boolean read FExeDrag;
  386. property ForwardCount: Integer read GetForwardCount;
  387. property HistoryPath[Index: Integer]: string read GetHistoryPath;
  388. property IsRoot: Boolean read GetIsRoot;
  389. property LastDDResult: TDragResult read FLastDDResult;
  390. property SmallImages;
  391. property LargeImages;
  392. property MaxHistoryCount: Integer read FMaxHistoryCount write SetMaxHistoryCount default DefaultHistoryCount;
  393. property SelectedNamesSaved: Boolean read GetSelectedNamesSaved;
  394. {filemask, multiple filters are possible: '*.pas;*.dfm'}
  395. property Mask: string read FMask write SetMask;
  396. property NaturalOrderNumericalSorting: Boolean read FNaturalOrderNumericalSorting write SetNaturalOrderNumericalSorting;
  397. property OnContextPopup;
  398. property OnStartLoading: TNotifyEvent read FOnStartLoading write FOnStartLoading;
  399. property OnLoaded: TNotifyEvent read FOnLoaded write FOnLoaded;
  400. {The mouse has entered the component window as a target of a drag&drop operation:}
  401. property OnDDDragEnter: TDDOnDragEnter read FOnDDDragEnter write FOnDDDragEnter;
  402. {The mouse has leaved the component window as a target of a drag&drop operation:}
  403. property OnDDDragLeave: TDDOnDragLeave read FOnDDDragLeave write FOnDDDragLeave;
  404. {The mouse is dragging in the component window as a target of a drag&drop operation:}
  405. property OnDDDragOver: TDDOnDragOver read FOnDDDragOver write FOnDDDragOver;
  406. {The Drag&drop operation is about to be executed:}
  407. property OnDDDrop: TDDOnDrop read FOnDDDrop write FOnDDDrop;
  408. property OnDDQueryContinueDrag: TDDOnQueryContinueDrag
  409. read FOnDDQueryContinueDrag write FOnDDQueryContinueDrag;
  410. property OnDDGiveFeedback: TDDOnGiveFeedback
  411. read FOnDDGiveFeedback write FOnDDGiveFeedback;
  412. property OnDDChooseEffect: TDDOnChooseEffect
  413. read FOnDDChooseEffect write FOnDDChooseEffect;
  414. {A drag&drop operation is about to be initiated whith
  415. the components window as the source:}
  416. property OnDDDragDetect: TDDOnDragDetect
  417. read FOnDDDragDetect write FOnDDDragDetect;
  418. property OnDDCreateDragFileList: TDDOnCreateDragFileList
  419. read FOnDDCreateDragFileList write FOnDDCreateDragFileList;
  420. property OnDDEnd: TNotifyEvent
  421. read FOnDDEnd write FOnDDEnd;
  422. property OnDDCreateDataObject: TDDOnCreateDataObject
  423. read FOnDDCreateDataObject write FOnDDCreateDataObject;
  424. property OnDDTargetHasDropHandler: TDDOnTargetHasDropHandler
  425. read FOnDDTargetHasDropHandler write FOnDDTargetHasDropHandler;
  426. {The component window is the target of a drag&drop operation:}
  427. property OnDDProcessDropped: TOnProcessDropped
  428. read FOnDDProcessDropped write FOnDDProcessDropped;
  429. {An error has occurred during a drag&drop operation:}
  430. property OnDDError: TDDErrorEvent read FOnDDError write FOnDDError;
  431. {The drag&drop operation has been executed:}
  432. property OnDDExecuted: TDDExecutedEvent
  433. read FOnDDExecuted write FOnDDExecuted;
  434. {Event is fired just before executing the fileoperation. This event is also fired when
  435. files are pasted from the clipboard:}
  436. property OnDDFileOperation: TDDFileOperationEvent
  437. read FOnDDFileOperation write FOnDDFileOperation;
  438. {Event is fired after executing the fileoperation. This event is also fired when
  439. files are pasted from the clipboard:}
  440. property OnDDFileOperationExecuted: TDDFileOperationExecutedEvent
  441. read FOnDDFileOperationExecuted write FOnDDFileOperationExecuted;
  442. {Set AllowExec to false, if actual file should not be executed:}
  443. property OnDDMenuPopup: TOnMenuPopup read FOnDDMenuPopup write FOnDDMenuPopup;
  444. property OnExecFile: TDirViewExecFileEvent
  445. read FOnExecFile write FOnExecFile;
  446. property OnHistoryChange: TDirViewNotifyEvent read FOnHistoryChange write FOnHistoryChange;
  447. property OnHistoryGo: TDVHistoryGoEvent read FOnHistoryGo write FOnHistoryGo;
  448. property OnPathChange: TDirViewNotifyEvent read FOnPathChange write FOnPathChange;
  449. property OnMatchMask: TMatchMaskEvent read FOnMatchMask write FOnMatchMask;
  450. property OnGetOverlay: TDirViewGetOverlayEvent read FOnGetOverlay write FOnGetOverlay;
  451. property PathLabel: TCustomPathLabel read FPathLabel write SetPathLabel;
  452. property ShowHiddenFiles: Boolean read FShowHiddenFiles write SetShowHiddenFiles default True;
  453. property OnUpdateStatusBar: TDirViewUpdateStatusBarEvent read FOnUpdateStatusBar write FOnUpdateStatusBar;
  454. property OnBusy: TDirViewBusy read FOnBusy write FOnBusy;
  455. {Watch current directory for filename changes (create, rename, delete files)}
  456. property WatchForChanges: Boolean read FWatchForChanges write SetWatchForChanges default False;
  457. end;
  458. resourcestring
  459. SErrorOpenFile = 'Can''t open file: ';
  460. SErrorRenameFile = 'Can''t rename file or directory: ';
  461. SErrorRenameFileExists = 'File already exists: ';
  462. SErrorInvalidName= 'Filename contains invalid characters:';
  463. STextFileExt = 'File %s';
  464. STextFiles = '%u Files';
  465. STextDirectories = '%u Directories';
  466. SParentDir = 'Parent directory';
  467. SIconUpdateThreadTerminationError = 'Can''t terminate icon update thread.';
  468. SDragDropError = 'DragDrop Error: %d';
  469. SDriveNotReady = 'Drive ''%s:'' is not ready.';
  470. SDirNotExists = 'Directory ''%s'' doesn''t exist.';
  471. {Additional non-component specific functions:}
  472. {Create and resolve a shell link (file shortcut):}
  473. function CreateFileShortCut(SourceFile, Target, DisplayName: string;
  474. UpdateIfExists: Boolean = False): Boolean;
  475. function ResolveFileShortCut(SourceFile: string; ShowDialog: Boolean = False): string;
  476. {Gets the shell's display icon for registered file extensions:}
  477. function GetIconIndex(const AFile: string; Attrs: DWORD; Flags: UINT): Integer;
  478. {Gets the shell's inforecord for registered fileextensions:}
  479. function GetshFileInfo(const AFile: string; Attrs: DWORD; Flags: UINT): TSHFileInfo;
  480. {Returns the displayname as used by the shell:}
  481. function GetShellDisplayName(const ShellFolder: IShellFolder; IDList: PItemIDList;
  482. Flags: DWORD; var Name: string): Boolean;
  483. function IsExecutable(FileName: string): Boolean;
  484. function GetNextMask(var Mask: string): string;
  485. procedure DefaultFileFilter(var Filter: TFileFilter);
  486. function OverlayImageList(Size: Integer): TImageList;
  487. var
  488. StdDirIcon: Integer;
  489. StdDirSelIcon: Integer;
  490. DropSourceControl: TObject;
  491. UnknownFileIcon: Integer = 0;
  492. StdDirTypeName: string;
  493. DefaultExeIcon: Integer;
  494. UserDocumentDirectory: string;
  495. implementation
  496. uses
  497. Math, DirViewColProperties, UITypes, Types, OperationWithTimeout;
  498. const
  499. Space = ' ';
  500. ResDirUp = 'DIRUP%2.2d';
  501. ResLink = 'LINK%2.2d';
  502. ResBrokenLink = 'BROKEN%2.2d';
  503. ResPartial = 'PARTIAL%2.2d';
  504. ResEncrypted = 'ENCRYPTED%2.2d';
  505. var
  506. WinDir: string;
  507. TempDir: string;
  508. GlobalsInitialized: Boolean = False;
  509. procedure InitGlobals;
  510. begin
  511. if not GlobalsInitialized then
  512. begin
  513. GlobalsInitialized := True;
  514. // Calling GetshFileInfo in Windows Session 0 sometime cause crash
  515. // (not immediately, but very shortly afterwards [few ms]).
  516. // So this code was moved from initialization section to avoid it
  517. // being used for non-GUI runs.
  518. UnknownFileIcon := GetshFileInfo('$#)(.#$)', FILE_ATTRIBUTE_NORMAL,
  519. SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES).iIcon;
  520. DefaultExeIcon := GetshFileInfo('.COM',
  521. FILE_ATTRIBUTE_NORMAL, SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES).iIcon;
  522. with GetshFileInfo(WinDir, FILE_ATTRIBUTE_NORMAL or FILE_ATTRIBUTE_DIRECTORY,
  523. SHGFI_TYPENAME or SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES) do
  524. begin
  525. StdDirTypeName := szTypeName;
  526. StdDirIcon := iIcon;
  527. end;
  528. StdDirSelIcon := GetIconIndex(WinDir,
  529. FILE_ATTRIBUTE_NORMAL or FILE_ATTRIBUTE_DIRECTORY, SHGFI_OPENICON);
  530. end;
  531. end;
  532. type
  533. TDirViewState = class(TObject)
  534. public
  535. destructor Destroy; override;
  536. private
  537. HistoryPaths: TStrings;
  538. BackCount: Integer;
  539. SortStr: string;
  540. Mask: string;
  541. FocusedItem: string;
  542. end;
  543. destructor TDirViewState.Destroy;
  544. begin
  545. HistoryPaths.Free;
  546. inherited;
  547. end;
  548. function IsExecutable(FileName: string): Boolean;
  549. var
  550. FileExt: string;
  551. begin
  552. FileExt := UpperCase(ExtractFileExt(FileName));
  553. Result := (FileExt = '.EXE') or (FileExt = '.COM');
  554. end;
  555. function GetNextMask(var Mask: string): string;
  556. var
  557. NextPos: Integer;
  558. begin
  559. NextPos := Pos(';', Mask);
  560. if NextPos = 0 then
  561. begin
  562. Result := Mask;
  563. SetLength(Mask, 0);
  564. end
  565. else
  566. begin
  567. Result := Copy(Mask, 1, NextPos - 1);
  568. Delete(Mask, 1, NextPos);
  569. end;
  570. end;
  571. procedure DefaultFileFilter(var Filter: TFileFilter);
  572. begin
  573. with Filter do
  574. begin
  575. SetLength(Masks, 0);
  576. Directories := False;
  577. end;
  578. end;
  579. { Shortcut-handling }
  580. function ResolveFileShortCut(SourceFile: string; ShowDialog: Boolean = False): string;
  581. var
  582. IUnk: IUnknown;
  583. HRes: HRESULT; // OLE-Operation Result
  584. SL: IShellLink; // Interface for ShellLink
  585. PF: IPersistFile; // Interface for PersistentFile
  586. SRec: TWIN32FINDDATA; // SearchRec of targetfile
  587. TargetDir: array[1..Max_Path] of Char; // Working directory of targetfile
  588. Flags: DWORD;
  589. begin
  590. Result := '';
  591. IUnk := CreateComObject(CLSID_ShellLink);
  592. SL := IUnk as IShellLink;
  593. PF := IUnk as IPersistFile;
  594. HRes := PF.Load(PChar(SourceFile), STGM_READ);
  595. if Succeeded(Hres) then
  596. begin
  597. if not ShowDialog then Flags := SLR_NOUPDATE or (1500 shl 8) or SLR_NO_UI
  598. else Flags := SLR_NOUPDATE;
  599. HRes := SL.Resolve(Application.Handle, Flags);
  600. if Succeeded(HRes) then
  601. begin
  602. HRes := SL.GetPath(@TargetDir, MAX_PATH, SRec, {SLGP_UNCPRIORITY}{SLGP_SHORTPATH} 0);
  603. if Succeeded(HRes) then
  604. Result := string(PChar(@TargetDir));
  605. end;
  606. end;
  607. end; {ResolveShortCut}
  608. function CreateFileShortCut(SourceFile, Target, DisplayName: string;
  609. UpdateIfExists: Boolean): Boolean;
  610. var
  611. IUnk: IUnknown;
  612. Hres: HRESULT;
  613. ShellLink: IShellLink; // Interface to ShellLink
  614. IPFile: IPersistFile; // Interface to PersistentFile
  615. TargetFile: string;
  616. begin
  617. Result := False;
  618. if Target = '' then TargetFile := SourceFile + '.lnk'
  619. else TargetFile := Target;
  620. IUnk := CreateComObject(CLSID_ShellLink);
  621. ShellLink := IUnk as IShellLink;
  622. IPFile := IUnk as IPersistFile;
  623. if FileExists(ApiPath(TargetFile)) and UpdateIfExists then
  624. begin
  625. HRes := IPFile.Load(PChar(TargetFile), 0);
  626. if not Succeeded(HRes) then Exit;
  627. end;
  628. with ShellLink do
  629. begin
  630. HRes := SetPath(PChar(SourceFile));
  631. if Succeeded(HRes) then
  632. HRes := SetWorkingDirectory(PChar(ExtractFilePath(SourceFile)));
  633. if Succeeded(HRes) and (DisplayName <> '') then
  634. HRes := SetDescription(PChar(DisplayName));
  635. end;
  636. if Succeeded(Hres) then
  637. begin
  638. HRes := IPFile.Save(PChar(TargetFile),False);
  639. if Succeeded(HRes) then Result := True;
  640. end;
  641. end; {CreateShortCut}
  642. function GetIconIndex(const AFile: string; Attrs: DWORD; Flags: UINT): Integer;
  643. var
  644. FileInfo: TSHFileInfo;
  645. begin
  646. try
  647. SHGetFileInfo(PChar(AFile), Attrs, FileInfo, SizeOf(TSHFileInfo),
  648. Flags or SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES);
  649. Result := FileInfo.iIcon;
  650. except
  651. Result := -1;
  652. end;
  653. end; {GetIconIndex}
  654. function GetshFileInfo(const AFile: string; Attrs: DWORD; Flags: UINT): TSHFileInfo;
  655. begin
  656. try
  657. SHGetFileInfoWithTimeout(PChar(AFile), Attrs, Result, SizeOf(TSHFileInfo), Flags, 1000);
  658. except
  659. FillChar(Result, SizeOf(Result), 0);
  660. end;
  661. end; {GetshFileInfo}
  662. function GetShellDisplayName(const ShellFolder: IShellFolder; IDList: PItemIDList;
  663. Flags: DWORD; var Name: string): Boolean;
  664. var
  665. Str: TStrRet;
  666. begin
  667. Result := True;
  668. Name := '';
  669. if ShellFolder.GetDisplayNameOf(IDList, Flags, Str) = NOERROR then
  670. begin
  671. case Str.uType of
  672. STRRET_WSTR: Name := WideCharToString(Str.pOleStr);
  673. STRRET_OFFSET: Name := PChar(UINT(IDList) + Str.uOffset);
  674. STRRET_CSTR: Name := string(Str.cStr);
  675. else Result := False;
  676. end;
  677. end
  678. else Result := False;
  679. end; {GetShellDisplayName}
  680. function OverlayImageList(Size: Integer): TImageList;
  681. procedure GetOverlayBitmap(ImageList: TImageList; BitmapName: string);
  682. var
  683. Bitmap: TBitmap;
  684. begin
  685. Bitmap := TBitmap.Create;
  686. try
  687. Bitmap.LoadFromResourceName(hInstance, BitmapName);
  688. ImageList.AddMasked(Bitmap, Bitmap.Canvas.Pixels[0, 0]);
  689. finally
  690. Bitmap.Free;
  691. end;
  692. end; {GetOverlayBitmap}
  693. begin
  694. // Hardcoded according to sizes of overlays we have in resources
  695. if Size >= 64 then Size := 64
  696. else
  697. if Size >= 48 then Size := 48
  698. else
  699. if Size >= 40 then Size := 40
  700. else
  701. if Size >= 32 then Size := 32
  702. else
  703. if Size >= 24 then Size := 24
  704. else
  705. if Size >= 20 then Size := 20
  706. else Size := 16;
  707. Result := TImageList.CreateSize(Size, Size);
  708. Result.DrawingStyle := dsTransparent;
  709. Result.BkColor := clNone;
  710. GetOverlayBitmap(Result, Format(ResDirUp, [Size]));
  711. GetOverlayBitmap(Result, Format(ResLink, [Size]));
  712. GetOverlayBitmap(Result, Format(ResBrokenLink, [Size]));
  713. GetOverlayBitmap(Result, Format(ResPartial, [Size]));
  714. GetOverlayBitmap(Result, Format(ResEncrypted, [Size]));
  715. end;
  716. { TLoadAnimationStartThread }
  717. {constructor TLoadAnimationStartThread.Create(AInterval: Integer; AAnimation: TAnimate);
  718. begin
  719. inherited Create(True);
  720. FInterval := AInterval;
  721. FAnimation := AAnimation;
  722. Resume;
  723. end;
  724. procedure TLoadAnimationStartThread.Execute;
  725. var
  726. XInterval: Integer;
  727. begin
  728. XInterval := FInterval;
  729. while (not Terminated) and (XInterval > 0) do
  730. begin
  731. Sleep(10);
  732. Dec(XInterval, 10);
  733. end;
  734. if (not Terminated) and Assigned(FAnimation) then
  735. Synchronize(StartAnimation);
  736. end;
  737. procedure TLoadAnimationStartThread.StartAnimation;
  738. begin
  739. FAnimation.Visible := True;
  740. FAnimation.Active := True;
  741. end; }
  742. { TCustomizableDragDropFilesEx }
  743. function TCustomizableDragDropFilesEx.Execute(DataObject: TDataObject): TDragResult;
  744. begin
  745. if not Assigned(DataObject) then
  746. begin
  747. DataObject := CreateDataObject;
  748. end;
  749. Result := ExecuteOperation(DataObject);
  750. end;
  751. { TCustomDirView }
  752. constructor TCustomDirView.Create(AOwner: TComponent);
  753. begin
  754. InitGlobals;
  755. inherited;
  756. FWatchForChanges := False;
  757. FFilesSize := 0;
  758. FFilesSelSize := 0;
  759. FDimmHiddenFiles := True;
  760. FShowHiddenFiles := True;
  761. FFormatSizeBytes := fbNone;
  762. FWantUseDragImages := False;
  763. FAddParentDir := False;
  764. FullDrag := True;
  765. FInvalidNameChars := '\/:*?"<>|';
  766. FHasParentDir := False;
  767. FDragOnDriveIsMove := False;
  768. FCaseSensitive := False;
  769. FLoadAnimation := True;
  770. FAnimation := nil;
  771. FIsRecycleBin := False;
  772. FLoading := False;
  773. FLoadEnabled := True;
  774. FAbortLoading := False;
  775. FDirty := False;
  776. FLastPath := '';
  777. FHistoryPath := '';
  778. FNotifyEnabled := True;
  779. FForceRename := False;
  780. FLastRenameName := '';
  781. FSavedSelection := False;
  782. FPendingFocusSomething := False;
  783. FSavedNames := TStringList.Create;
  784. FContextMenu := False;
  785. FUseSystemContextMenu := True;
  786. FStartPos.X := -1;
  787. FStartPos.Y := -1;
  788. FDragPos := FStartPos;
  789. FDragEnabled := False;
  790. FDDOwnerIsSource := False;
  791. FDDLinkOnExeDrag := False;
  792. FDragDrive := '';
  793. FExeDrag := False;
  794. FMask := '';
  795. FNaturalOrderNumericalSorting := True;
  796. FDoubleBufferedScrollingWorkaround := not IsVistaHard();
  797. FOnHistoryChange := nil;
  798. FOnPathChange := nil;
  799. FHistoryPaths := TStringList.Create;
  800. FBackCount := 0;
  801. FDontRecordPath := False;
  802. FMaxHistoryCount := DefaultHistoryCount;
  803. FStatusFileInfo.FilesCount := -1;
  804. OnCustomDrawItem := DumbCustomDrawItem;
  805. OnCustomDrawSubItem := DumbCustomDrawSubItem;
  806. FOnMatchMask := nil;
  807. FOnGetOverlay := nil;
  808. FDragDropFilesEx := TCustomizableDragDropFilesEx.Create(Self);
  809. with FDragDropFilesEx do
  810. begin
  811. AutoDetectDnD := False;
  812. DragDetectDelta := 4;
  813. AcceptOwnDnD := True;
  814. BringToFront := True;
  815. CompleteFileList := True;
  816. NeedValid := [nvFileName];
  817. RenderDataOn := rdoEnterAndDropSync;
  818. TargetPopUpMenu := True;
  819. SourceEffects := DragSourceEffects;
  820. TargetEffects := [deCopy, deMove];
  821. OnDragEnter := DDDragEnter;
  822. OnDragLeave := DDDragLeave;
  823. OnDragOver := DDDragOver;
  824. OnDrop := DDDrop;
  825. OnQueryContinueDrag := DDQueryContinueDrag;
  826. OnSpecifyDropTarget := DDSpecifyDropTarget;
  827. OnMenuPopup := DDMenuPopup;
  828. OnMenuDestroy := DDMenuDone;
  829. OnDropHandlerSucceeded := DDDropHandlerSucceeded;
  830. OnGiveFeedback := DDGiveFeedback;
  831. OnProcessDropped := DDProcessDropped;
  832. OnDragDetect := DDDragDetect;
  833. end;
  834. FScrollOnDragOver := TListViewScrollOnDragOver.Create(Self, False);
  835. FScrollOnDragOver.OnBeforeUpdate := ScrollOnDragOverBeforeUpdate;
  836. FScrollOnDragOver.OnAfterUpdate := ScrollOnDragOverAfterUpdate;
  837. end;
  838. procedure TCustomDirView.ClearItems;
  839. begin
  840. CancelEdit;
  841. if Assigned(DropTarget) then DropTarget := nil;
  842. try
  843. inherited;
  844. finally
  845. FFilesSelSize := 0;
  846. FFilesSize := 0;
  847. UpdateStatusBar;
  848. end;
  849. end;
  850. procedure TCustomDirView.CNNotify(var Message: TWMNotify);
  851. procedure DrawOverlayImage(DC: HDC; Image: Integer);
  852. var
  853. ImageList: TCustomImageList;
  854. Rect: TRect;
  855. Point: TPoint;
  856. Index: Integer;
  857. begin
  858. Rect := Items[PNMCustomDraw(Message.NMHdr)^.dwItemSpec].DisplayRect(drIcon);
  859. Point := Rect.TopLeft;
  860. if ViewStyle = vsIcon then
  861. begin
  862. ImageList := ImageList32;
  863. end
  864. else
  865. begin
  866. ImageList := ImageList16;
  867. end;
  868. // center on the rect
  869. Inc(Point.X, (Rect.Width - ImageList.Width) div 2);
  870. Inc(Point.Y, (Rect.Height - ImageList.Height) div 2);
  871. Index := 0;
  872. while Image > 1 do
  873. begin
  874. Inc(Index);
  875. Image := Image shr 1;
  876. end;
  877. if 8 + ImageList.Width <= Columns[0].Width then
  878. begin
  879. ImageList_Draw(ImageList.Handle, Index, DC,
  880. Point.X, Point.Y, ILD_TRANSPARENT);
  881. end;
  882. end;
  883. var
  884. FileSize: Int64;
  885. Item: TListItem;
  886. InfoMask: LongWord;
  887. OverlayIndex: Word;
  888. OverlayIndexes: Word;
  889. UpdateStatusBarPending: Boolean;
  890. begin
  891. UpdateStatusBarPending := False;
  892. case Message.NMHdr^.code of
  893. LVN_ITEMCHANGED:
  894. with PNMListView(Message.NMHdr)^ do
  895. if (uChanged = LVIF_STATE) and Valid and (not FClearingItems) then
  896. begin
  897. if ((uOldState and (LVIS_SELECTED or LVIS_FOCUSED)) <>
  898. (uNewState and (LVIS_SELECTED or LVIS_FOCUSED))) then
  899. UpdateStatusBarPending := True;
  900. if ((uOldState and LVIS_SELECTED) <> (uNewState and LVIS_SELECTED)) then
  901. begin
  902. FileSize := ItemFileSize(Items[iItem]);
  903. if (uOldState and LVIS_SELECTED) <> 0 then Dec(FFilesSelSize, FileSize)
  904. else Inc(FFilesSelSize, FileSize);
  905. end;
  906. end;
  907. LVN_ENDLABELEDIT:
  908. // enable loading now only when editing was canceled.
  909. // when it was confirmed, it will be enabled only after actual
  910. // file renaming is completed. see Edit().
  911. with PLVDispInfo(Message.NMHdr)^ do
  912. if (item.pszText = nil) or (item.IItem = -1) then
  913. LoadEnabled := True;
  914. LVN_BEGINDRAG:
  915. if FDragEnabled and (not Loading) then
  916. begin
  917. DDBeforeDrag;
  918. DDDragDetect(MK_LBUTTON, FStartPos, Mouse.CursorPos, ddsDrag);
  919. end;
  920. LVN_BEGINRDRAG:
  921. if FDragEnabled and (not Loading) then
  922. begin
  923. DDBeforeDrag;
  924. DDDragDetect(MK_RBUTTON, FStartPos, Mouse.CursorPos, ddsDrag);
  925. end;
  926. end;
  927. inherited;
  928. if (Message.NMHdr.code = LVN_GETDISPINFO) and
  929. FNotifyEnabled and Valid and (not Loading) then
  930. with PLVDispInfo(Pointer(Message.NMHdr))^.Item do
  931. try
  932. InfoMask := PLVDispInfo(Pointer(Message.NMHdr))^.item.Mask;
  933. if (InfoMask and LVIF_PARAM) <> 0 then Item := TListItem(lParam)
  934. else
  935. if iItem < Items.Count then Item := Items[iItem]
  936. else Item := nil;
  937. if Assigned(Item) and Assigned(Item.Data) then
  938. GetDisplayInfo(Item, PLVDispInfo(Pointer(Message.NMHdr))^.item);
  939. except
  940. end;
  941. if (Message.NMHdr.code = NM_CUSTOMDRAW) and
  942. Valid and (not Loading) then
  943. with PNMLVCustomDraw(Message.NMHdr)^ do
  944. try
  945. Message.Result := Message.Result or CDRF_NOTIFYPOSTPAINT;
  946. if (nmcd.dwDrawStage = CDDS_ITEMPOSTPAINT) and
  947. ((nmcd.dwDrawStage and CDDS_SUBITEM) = 0) and
  948. Assigned(Columns[0]) and (Columns[0].Width > 0) then
  949. begin
  950. Assert(Assigned(Items[nmcd.dwItemSpec]));
  951. OverlayIndexes := ItemOverlayIndexes(Items[nmcd.dwItemSpec]);
  952. OverlayIndex := 1;
  953. while OverlayIndexes > 0 do
  954. begin
  955. if (OverlayIndex and OverlayIndexes) <> 0 then
  956. begin
  957. DrawOverlayImage(nmcd.hdc, OverlayIndex);
  958. Dec(OverlayIndexes, OverlayIndex);
  959. end;
  960. OverlayIndex := OverlayIndex shl 1;
  961. end;
  962. end;
  963. except
  964. end;
  965. if UpdateStatusBarPending then UpdateStatusBar;
  966. end;
  967. function TCustomDirView.FileNameMatchesMasks(FileName: string;
  968. Directory: Boolean; Size: Int64; Modification: TDateTime; Masks: string;
  969. AllowImplicitMatches: Boolean): Boolean;
  970. begin
  971. Result := False;
  972. if Assigned(OnMatchMask) then
  973. OnMatchMask(Self, FileName, Directory, Size, Modification, Masks, Result, AllowImplicitMatches)
  974. end;
  975. procedure TCustomDirView.SetAddParentDir(Value: Boolean);
  976. begin
  977. if FAddParentDir <> Value then
  978. begin
  979. FAddParentDir := Value;
  980. if DirOK then Reload(True);
  981. end;
  982. end;
  983. procedure TCustomDirView.SetDimmHiddenFiles(Value: Boolean);
  984. begin
  985. if Value <> FDimmHiddenFiles then
  986. begin
  987. FDimmHiddenFiles := Value;
  988. Self.Repaint;
  989. end;
  990. end; {SetDimmHiddenFiles}
  991. procedure TCustomDirView.SetPathLabel(Value: TCustomPathLabel);
  992. begin
  993. if FPathLabel <> Value then
  994. begin
  995. if Assigned(FPathLabel) and (FPathLabel.FocusControl = Self) then
  996. FPathLabel.FocusControl := nil;
  997. FPathLabel := Value;
  998. if Assigned(Value) then
  999. begin
  1000. Value.FreeNotification(Self);
  1001. if not Assigned(Value.FocusControl) then
  1002. Value.FocusControl := Self;
  1003. UpdatePathLabel;
  1004. end;
  1005. end;
  1006. end; { SetPathLabel }
  1007. procedure TCustomDirView.SetShowHiddenFiles(Value: Boolean);
  1008. begin
  1009. if ShowHiddenFiles <> Value then
  1010. begin
  1011. FShowHiddenFiles := Value;
  1012. if DirOK then Reload(False);
  1013. end;
  1014. end;
  1015. procedure TCustomDirView.SetFormatSizeBytes(Value: TFormatBytesStyle);
  1016. begin
  1017. if Value <> FFormatSizeBytes then
  1018. begin
  1019. FFormatSizeBytes := Value;
  1020. Self.Repaint;
  1021. end;
  1022. end; {SetFormatSizeBytes}
  1023. function TCustomDirView.GetDragSourceEffects: TDropEffectSet;
  1024. begin
  1025. Result := [deCopy, deMove, deLink];
  1026. end;
  1027. function TCustomDirView.GetUseDragImages: Boolean;
  1028. begin
  1029. Result := FWantUseDragImages;
  1030. end;
  1031. procedure TCustomDirView.SetTargetPopupMenu(Value: Boolean);
  1032. begin
  1033. if Assigned(FDragDropFilesEx) then FDragDropFilesEx.TargetPopupMenu := Value;
  1034. end;
  1035. procedure TCustomDirView.NeedImageLists(Recreate: Boolean);
  1036. begin
  1037. SmallImages := ShellImageListForControl(Self, ilsSmall);
  1038. LargeImages := ShellImageListForControl(Self, ilsLarge);
  1039. if (not Assigned(FImageList16)) or Recreate then
  1040. begin
  1041. FreeAndNil(FImageList16);
  1042. FImageList16 := OverlayImageList(SmallImages.Width);
  1043. end;
  1044. if (not Assigned(FImageList32)) or Recreate then
  1045. begin
  1046. FreeAndNil(FImageList32);
  1047. FImageList32 := OverlayImageList(LargeImages.Width);
  1048. end;
  1049. end;
  1050. procedure TCustomDirView.CMDPIChanged(var Message: TMessage);
  1051. begin
  1052. inherited;
  1053. NeedImageLists(True);
  1054. end;
  1055. procedure TCustomDirView.FreeImageLists;
  1056. begin
  1057. FreeAndNil(FImageList16);
  1058. FreeAndNil(FImageList32);
  1059. SmallImages := nil;
  1060. LargeImages := nil;
  1061. end;
  1062. procedure TCustomDirView.CreateWnd;
  1063. begin
  1064. inherited;
  1065. if Assigned(PopupMenu) then
  1066. PopupMenu.Autopopup := False;
  1067. FDragDropFilesEx.DragDropControl := Self;
  1068. NeedImageLists(False);
  1069. end;
  1070. procedure TCustomDirView.LVMSetExtendedListViewStyle(var Message: TMessage);
  1071. // Only TWinControl.DoubleBuffered actually prevents flicker
  1072. // on Win7 when moving mouse over list view, not LVS_EX_DOUBLEBUFFER.
  1073. // But LVS_EX_DOUBLEBUFFER brings nice alpha blended marquee selection.
  1074. // Double buffering introduces artefacts when scrolling using
  1075. // keyboard (Page-up/Down). This gets fixed by LVS_EX_TRANSPARENTBKGND,
  1076. // but that works on Vista and newer only. See WMKeyDown
  1077. // for workaround on earlier systems.
  1078. const
  1079. RequiredStyles = LVS_EX_DOUBLEBUFFER or LVS_EX_TRANSPARENTBKGND;
  1080. begin
  1081. // This prevents TCustomListView.ResetExStyles resetting our styles
  1082. if (Message.WParam = 0) and
  1083. ((Message.LParam and RequiredStyles) <> RequiredStyles) then
  1084. begin
  1085. ListView_SetExtendedListViewStyle(Handle, Message.LParam or RequiredStyles);
  1086. end
  1087. else
  1088. begin
  1089. inherited;
  1090. end;
  1091. end;
  1092. procedure TCustomDirView.DestroyWnd;
  1093. begin
  1094. // to force drag&drop re-registration when recreating handle
  1095. // (occurs when changing ViewStyle)
  1096. FDragDropFilesEx.DragDropControl := nil;
  1097. // Destroy the animation, as we keep getting reports that the animation fails to recreate
  1098. DoAnimation(False);
  1099. inherited;
  1100. end;
  1101. procedure TCustomDirView.CMRecreateWnd(var Message: TMessage);
  1102. var
  1103. HadHandle: Boolean;
  1104. begin
  1105. HadHandle := HandleAllocated;
  1106. inherited;
  1107. // See comment in TCustomDriveView.CMRecreateWnd
  1108. if HadHandle then
  1109. begin
  1110. HandleNeeded;
  1111. end;
  1112. end;
  1113. function TCustomDirView.CustomDrawItem(Item: TListItem; State: TCustomDrawState;
  1114. Stage: TCustomDrawStage): Boolean;
  1115. var
  1116. FItemColor: TColor;
  1117. begin
  1118. if (Item <> nil) and (Stage = cdPrePaint) then
  1119. begin
  1120. FItemColor := ItemColor(Item);
  1121. if (FItemColor <> clDefaultItemColor) and
  1122. (Canvas.Font.Color <> FItemColor) then
  1123. Canvas.Font.Color := FItemColor;
  1124. end;
  1125. Result := inherited CustomDrawItem(Item, State, Stage);
  1126. end;
  1127. function TCustomDirView.CustomDrawSubItem(Item: TListItem; SubItem: Integer;
  1128. State: TCustomDrawState; Stage: TCustomDrawStage): Boolean;
  1129. var
  1130. FItemColor: TColor;
  1131. begin
  1132. if Stage = cdPrePaint then
  1133. begin
  1134. FItemColor := ItemColor(Item);
  1135. if (FItemColor <> clDefaultItemColor) and
  1136. (Canvas.Font.Color <> FItemColor) then
  1137. Canvas.Font.Color := FItemColor;
  1138. end;
  1139. Result := inherited CustomDrawSubItem(Item, SubItem, State, Stage);
  1140. end;
  1141. procedure TCustomDirView.Delete(Item: TListItem);
  1142. begin
  1143. if Assigned(Item) then
  1144. begin
  1145. // This causes access violation when size is stored in structure
  1146. // pointed by TListItem->Data and this structure is not valid any more
  1147. if Valid then Dec(FFilesSize, ItemFileSize(Item));
  1148. inherited Delete(Item);
  1149. end;
  1150. end;
  1151. destructor TCustomDirView.Destroy;
  1152. begin
  1153. Assert(not FSavedSelection);
  1154. FreeAndNil(FScrollOnDragOver);
  1155. FreeAndNil(FSavedNames);
  1156. FreeAndNil(FHistoryPaths);
  1157. FreeAndNil(FDragDropFilesEx);
  1158. FreeImageLists;
  1159. FreeAndNil(FAnimation);
  1160. inherited;
  1161. end;
  1162. procedure TCustomDirView.SelectFiles(Filter: TFileFilter; Select: Boolean);
  1163. var
  1164. Item: TListItem;
  1165. Index: Integer;
  1166. OldCursor: TCursor;
  1167. begin
  1168. Assert(Valid);
  1169. OldCursor := Screen.Cursor;
  1170. Items.BeginUpdate;
  1171. BeginSelectionUpdate;
  1172. try
  1173. Screen.Cursor := crHourGlass;
  1174. for Index := 0 to Items.Count-1 do
  1175. begin
  1176. Item := Items[Index];
  1177. Assert(Assigned(Item));
  1178. if (Item.Selected <> Select) and
  1179. ItemMatchesFilter(Item, Filter) then
  1180. Item.Selected := Select;
  1181. end;
  1182. finally
  1183. Screen.Cursor := OldCursor;
  1184. Items.EndUpdate;
  1185. EndSelectionUpdate;
  1186. end;
  1187. end;
  1188. function TCustomDirView.DragCompleteFileList: Boolean;
  1189. begin
  1190. Result := (MarkedCount <= 100) and (not IsRecycleBin);
  1191. end;
  1192. procedure TCustomDirView.DumbCustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
  1193. begin
  1194. end;
  1195. procedure TCustomDirView.DumbCustomDrawSubItem(Sender: TCustomListView;
  1196. Item: TListItem; SubItem: Integer; State: TCustomDrawState;
  1197. var DefaultDraw: Boolean);
  1198. begin
  1199. end;
  1200. function TCustomDirView.GetTargetPopupMenu: Boolean;
  1201. begin
  1202. if Assigned(FDragDropFilesEx) then Result := FDragDropFilesEx.TargetPopupMenu
  1203. else Result := True;
  1204. end;
  1205. procedure TCustomDirView.SetMultiSelect(Value: Boolean);
  1206. begin
  1207. if Value <> MultiSelect then
  1208. begin
  1209. inherited SetMultiSelect(Value);
  1210. if not (csLoading in ComponentState) and Assigned(ColProperties) then
  1211. begin
  1212. ColProperties.RecreateColumns;
  1213. SetColumnImages;
  1214. if DirOK then Reload(True);
  1215. end;
  1216. end;
  1217. end;
  1218. function TCustomDirView.GetValid: Boolean;
  1219. begin
  1220. Result := (not (csDestroying in ComponentState)) and
  1221. (not Loading) and (not FClearingItems);
  1222. end;
  1223. function TCustomDirView.ItemCanDrag(Item: TListItem): Boolean;
  1224. begin
  1225. Result := (not ItemIsParentDirectory(Item));
  1226. end;
  1227. function TCustomDirView.ItemColor(Item: TListItem): TColor;
  1228. begin
  1229. Result := clDefaultItemColor;
  1230. end;
  1231. function TCustomDirView.GetFilesMarkedSize: Int64;
  1232. begin
  1233. if SelCount > 0 then Result := FilesSelSize
  1234. else
  1235. if Assigned(ItemFocused) then Result := ItemFileSize(ItemFocused)
  1236. else Result := 0;
  1237. end;
  1238. function TCustomDirView.ItemIsRecycleBin(Item: TListItem): Boolean;
  1239. begin
  1240. Result := False;
  1241. end;
  1242. function TCustomDirView.ItemOverlayIndexes(Item: TListItem): Word;
  1243. begin
  1244. Result := oiNoOverlay;
  1245. if Assigned(OnGetOverlay) then
  1246. OnGetOverlay(Self, Item, Result);
  1247. end;
  1248. procedure TCustomDirView.WMKeyDown(var Message: TWMKeyDown);
  1249. begin
  1250. if DoubleBuffered and (Message.CharCode in [VK_PRIOR, VK_NEXT]) and
  1251. FDoubleBufferedScrollingWorkaround then
  1252. begin
  1253. // WORKAROUND
  1254. // When scrolling with double-buffering enabled, ugly artefacts
  1255. // are shown temporarily.
  1256. // LVS_EX_TRANSPARENTBKGND fixes it on Vista and newer
  1257. SendMessage(Handle, WM_SETREDRAW, 0, 0);
  1258. try
  1259. inherited;
  1260. finally
  1261. SendMessage(Handle, WM_SETREDRAW, 1, 0);
  1262. end;
  1263. Repaint;
  1264. end
  1265. else
  1266. begin
  1267. inherited;
  1268. end;
  1269. end;
  1270. procedure TCustomDirView.DoDisplayPropertiesMenu;
  1271. begin
  1272. if not IsBusy then
  1273. DisplayPropertiesMenu;
  1274. end;
  1275. procedure TCustomDirView.DoExecute(Item: TListItem);
  1276. begin
  1277. BusyOperation(procedure begin Execute(Item); end);
  1278. end;
  1279. procedure TCustomDirView.DoExecuteParentDirectory;
  1280. begin
  1281. BusyOperation(ExecuteParentDirectory);
  1282. end;
  1283. procedure TCustomDirView.KeyDown(var Key: Word; Shift: TShiftState);
  1284. var
  1285. AKey: Word;
  1286. begin
  1287. if Valid and (not IsEditing) and (not Loading) then
  1288. begin
  1289. if (Key = VK_RETURN) or
  1290. ((Key = VK_NEXT) and (ssCtrl in Shift)) then
  1291. begin
  1292. if Assigned(ItemFocused) then
  1293. begin
  1294. AKey := Key;
  1295. Key := 0;
  1296. if (AKey = VK_RETURN) and (Shift = [ssAlt]) then DoDisplayPropertiesMenu
  1297. else
  1298. if (AKey <> VK_RETURN) or (Shift = []) then DoExecute(ItemFocused);
  1299. end;
  1300. end
  1301. else
  1302. if ((Key = VK_BACK) or ((Key = VK_PRIOR) and (ssCtrl in Shift))) and
  1303. (not IsRoot) then
  1304. begin
  1305. Key := 0;
  1306. DoExecuteParentDirectory;
  1307. end
  1308. else
  1309. if ((Key = VK_UP) and (ssAlt in Shift)) and
  1310. (not IsRoot) then
  1311. begin
  1312. Key := 0;
  1313. // U+25D8 is 'INVERSE BULLET', what is glyph representing '\x8' (or '\b')
  1314. // ('up' key is the '8' key on numeric pad)
  1315. // We could obtain the value programatically using
  1316. // MultiByteToWideChar(CP_OEMCP, MB_USEGLYPHCHARS, "\x8", 1, ...)
  1317. FNextCharToIgnore := $25D8;
  1318. DoExecuteParentDirectory;
  1319. end
  1320. else
  1321. if (Key = 220 { backslash }) and (ssCtrl in Shift) and (not IsRoot) then
  1322. begin
  1323. Key := 0;
  1324. BusyOperation(ExecuteRootDirectory);
  1325. end
  1326. else
  1327. if (Key = VK_LEFT) and (ssAlt in Shift) then
  1328. begin
  1329. if BackCount >= 1 then DoHistoryGo(-1);
  1330. end
  1331. else
  1332. if (Key = VK_RIGHT) and (ssAlt in Shift) then
  1333. begin
  1334. if ForwardCount >= 1 then DoHistoryGo(1);
  1335. end
  1336. else
  1337. begin
  1338. inherited;
  1339. end;
  1340. end
  1341. else
  1342. begin
  1343. inherited;
  1344. end;
  1345. end;
  1346. procedure TCustomDirView.KeyPress(var Key: Char);
  1347. begin
  1348. if IsEditing and (Pos(Key, FInvalidNameChars) <> 0) Then
  1349. begin
  1350. Beep;
  1351. Key := #0;
  1352. end;
  1353. inherited;
  1354. end;
  1355. procedure TCustomDirView.DisplayContextMenuInSitu;
  1356. var
  1357. R: TRect;
  1358. P: TPoint;
  1359. begin
  1360. if Assigned(ItemFocused) then
  1361. begin
  1362. R := ItemFocused.DisplayRect(drIcon);
  1363. P.X := (R.Left + R.Right) div 2;
  1364. P.Y := (R.Top + R.Bottom) div 2;
  1365. end
  1366. else
  1367. begin
  1368. P.X := 0;
  1369. P.Y := 0;
  1370. end;
  1371. P := ClientToScreen(P);
  1372. DisplayContextMenu(P);
  1373. end;
  1374. procedure TCustomDirView.KeyUp(var Key: Word; Shift: TShiftState);
  1375. var
  1376. P: TPoint;
  1377. begin
  1378. if Key = VK_APPS then
  1379. begin
  1380. if (not Loading) and (not IsBusy) then
  1381. begin
  1382. if MarkedCount > 0 then
  1383. begin
  1384. DisplayContextMenuInSitu;
  1385. end
  1386. else
  1387. if Assigned(PopupMenu) then
  1388. begin
  1389. P.X := 0;
  1390. P.Y := 0;
  1391. P := ClientToScreen(P);
  1392. PopupMenu.Popup(P.X, P.Y);
  1393. end;
  1394. end;
  1395. end
  1396. else
  1397. inherited KeyUp(Key, Shift);
  1398. end;
  1399. procedure TCustomDirView.SetWatchForChanges(Value: Boolean);
  1400. begin
  1401. if FWatchForChanges <> Value then
  1402. FWatchForChanges := Value;
  1403. end;
  1404. function TCustomDirView.TargetHasDropHandler(Item: TListItem; Effect: Integer): Boolean;
  1405. begin
  1406. Assert(Assigned(DragDropFilesEx) and Assigned(Item));
  1407. Result :=
  1408. DragDropFilesEx.TargetHasDropHandler(nil, ItemFullFileName(Item), Effect);
  1409. if Assigned(OnDDTargetHasDropHandler) then
  1410. begin
  1411. OnDDTargetHasDropHandler(Self, Item, Effect, Result);
  1412. end;
  1413. end;
  1414. procedure TCustomDirView.UpdatePathLabelCaption;
  1415. begin
  1416. PathLabel.Caption := PathName;
  1417. PathLabel.Mask := Mask;
  1418. end;
  1419. procedure TCustomDirView.UpdatePathLabel;
  1420. begin
  1421. if Assigned(PathLabel) then
  1422. begin
  1423. if csDesigning in ComponentState then
  1424. begin
  1425. PathLabel.Caption := PathLabel.Name;
  1426. PathLabel.Mask := '';
  1427. end
  1428. else
  1429. begin
  1430. UpdatePathLabelCaption;
  1431. end;
  1432. PathLabel.UpdateStatus;
  1433. end;
  1434. end; { UpdatePathLabel }
  1435. procedure TCustomDirView.UpdateStatusBar;
  1436. var
  1437. StatusFileInfo: TStatusFileInfo;
  1438. begin
  1439. if (FUpdatingSelection = 0) and Assigned(OnUpdateStatusBar) then
  1440. begin
  1441. with StatusFileInfo do
  1442. begin
  1443. SelectedSize := FilesSelSize;
  1444. FilesSize := Self.FilesSize;
  1445. SelectedCount := SelCount;
  1446. FilesCount := Self.FilesCount;
  1447. HiddenCount := Self.HiddenCount;
  1448. FilteredCount := Self.FilteredCount;
  1449. end;
  1450. if not CompareMem(@StatusFileInfo, @FStatusFileInfo, SizeOf(StatusFileInfo)) then
  1451. begin
  1452. FStatusFileInfo := StatusFileInfo;
  1453. OnUpdateStatusBar(Self, FStatusFileInfo);
  1454. end;
  1455. end;
  1456. end; { UpdateStatusBar }
  1457. procedure TCustomDirView.WMContextMenu(var Message: TWMContextMenu);
  1458. var
  1459. Point: TPoint;
  1460. begin
  1461. FDragEnabled := False;
  1462. if Assigned(PopupMenu) then
  1463. PopupMenu.AutoPopup := False;
  1464. //inherited;
  1465. if FContextMenu and (not Loading) then
  1466. begin
  1467. Point.X := Message.XPos;
  1468. Point.Y := Message.YPos;
  1469. Point := ScreenToClient(Point);
  1470. if Assigned(OnMouseDown) then
  1471. begin
  1472. OnMouseDown(Self, mbRight, [], Point.X, Point.Y);
  1473. end;
  1474. if FUseSystemContextMenu and Assigned(ItemFocused) and
  1475. (GetItemAt(Point.X, Point.Y) = ItemFocused) then
  1476. begin
  1477. Point.X := Message.XPos;
  1478. Point.Y := Message.YPos;
  1479. DisplayContextMenu(Point);
  1480. end
  1481. else
  1482. if Assigned(PopupMenu) and (not PopupMenu.AutoPopup) then
  1483. begin
  1484. PopupMenu.Popup(Message.XPos, Message.YPos);
  1485. end;
  1486. end;
  1487. FContextMenu := False;
  1488. //inherited;
  1489. end;
  1490. function TCustomDirView.EnableDragOnClick: Boolean;
  1491. begin
  1492. Result := (not Loading) and inherited EnableDragOnClick;
  1493. end;
  1494. procedure TCustomDirView.WMLButtonDown(var Message: TWMLButtonDown);
  1495. begin
  1496. GetCursorPos(FStartPos);
  1497. FDragEnabled := EnableDragOnClick;
  1498. inherited;
  1499. end;
  1500. procedure TCustomDirView.WMRButtonDown(var Message: TWMRButtonDown);
  1501. begin
  1502. GetCursorPos(FStartPos);
  1503. if FDragDropFilesEx.DragDetectStatus <> ddsDrag then
  1504. FDragEnabled := EnableDragOnClick;
  1505. FContextMenu := True;
  1506. inherited;
  1507. end;
  1508. procedure TCustomDirView.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  1509. begin
  1510. inherited;
  1511. if Assigned(ItemFocused) and (not Loading) and
  1512. (GetItemAt(Message.XPos, Message.YPos) = ItemFocused) then
  1513. begin
  1514. if GetKeyState(VK_MENU) < 0 then DoDisplayPropertiesMenu
  1515. else DoExecute(ItemFocused);
  1516. end;
  1517. end;
  1518. procedure TCustomDirView.WMLButtonUp(var Message: TWMLButtonUp);
  1519. begin
  1520. FDragEnabled := False;
  1521. inherited;
  1522. end;
  1523. procedure TCustomDirView.WMXButtonUp(var Message: TWMXMouse);
  1524. begin
  1525. if Message.Button = _XBUTTON1 then
  1526. begin
  1527. if BackCount >= 1 then DoHistoryGo(-1);
  1528. Message.Result := 1;
  1529. end
  1530. else
  1531. if Message.Button = _XBUTTON2 then
  1532. begin
  1533. if ForwardCount >= 1 then DoHistoryGo(1);
  1534. Message.Result := 1;
  1535. end;
  1536. end;
  1537. procedure TCustomDirView.CancelEdit;
  1538. begin
  1539. // - Do nothing when handle is not allocated (we cannot be editing anyway
  1540. // without a handle), otherwise this causes handle allocation,
  1541. // what is wrong particularly when we are called from ClearItems
  1542. // when we are being destroyed
  1543. // - If editing, it has to be focused item
  1544. if HandleAllocated and IsEditing and Assigned(ItemFocused) then
  1545. begin
  1546. ItemFocused.CancelEdit;
  1547. FLoadEnabled := True;
  1548. end;
  1549. end;
  1550. procedure TCustomDirView.Reload(CacheIcons: Boolean);
  1551. var
  1552. OldSelection: TStringList;
  1553. OldItemFocused: string;
  1554. OldFocusedShown: Boolean;
  1555. OldShownItemOffset: Integer;
  1556. Index: Integer;
  1557. FoundIndex: Integer;
  1558. IconCache: TStringList;
  1559. Item: TListItem;
  1560. ItemToFocus: TListItem;
  1561. FileName: string;
  1562. R: TRect;
  1563. P: TPoint;
  1564. begin
  1565. if Path <> '' then
  1566. begin
  1567. CancelEdit;
  1568. OldSelection := nil;
  1569. IconCache := nil;
  1570. Items.BeginUpdate;
  1571. try
  1572. OldSelection := TStringList.Create;
  1573. OldSelection.CaseSensitive := FCaseSensitive;
  1574. if CacheIcons then
  1575. IconCache := TStringList.Create;
  1576. for Index := 0 to Items.Count-1 do
  1577. begin
  1578. Item := Items[Index];
  1579. // cannot use ItemFileName as for TUnixDirView the file object
  1580. // is no longer valid
  1581. FileName := Item.Caption;
  1582. if Item.Selected then
  1583. OldSelection.Add(FileName);
  1584. if CacheIcons and (ItemImageIndex(Item, True) >= 0) then
  1585. IconCache.AddObject(FileName, TObject(ItemImageIndex(Item, True)));
  1586. end;
  1587. if FSelectFile <> '' then
  1588. begin
  1589. OldItemFocused := FSelectFile;
  1590. OldFocusedShown := False;
  1591. OldShownItemOffset := -1;
  1592. FSelectFile := '';
  1593. end
  1594. else
  1595. begin
  1596. if Assigned(ItemFocused) then
  1597. begin
  1598. if ViewStyle = vsReport then
  1599. begin
  1600. if Assigned(TopItem) then
  1601. begin
  1602. R := ItemFocused.DisplayRect(drBounds);
  1603. if (R.Top < TopItem.DisplayRect(drBounds).Top) or (R.Top > ClientHeight) then
  1604. begin
  1605. OldFocusedShown := False;
  1606. OldShownItemOffset := TopItem.Index;
  1607. end
  1608. else
  1609. begin
  1610. OldFocusedShown := True;
  1611. OldShownItemOffset := ItemFocused.Index - TopItem.Index;
  1612. end;
  1613. end
  1614. else
  1615. begin
  1616. // seen with one user only
  1617. OldFocusedShown := False;
  1618. OldShownItemOffset := 0;
  1619. end;
  1620. end
  1621. else
  1622. begin
  1623. // to satisfy compiler, never used
  1624. OldFocusedShown := False;
  1625. OldShownItemOffset := -1;
  1626. end;
  1627. OldItemFocused := ItemFocused.Caption;
  1628. end
  1629. else
  1630. begin
  1631. OldItemFocused := '';
  1632. OldFocusedShown := False;
  1633. if Assigned(TopItem) then OldShownItemOffset := TopItem.Index
  1634. else OldShownItemOffset := -1;
  1635. end;
  1636. end;
  1637. Load(False);
  1638. OldSelection.Sort;
  1639. if CacheIcons then IconCache.Sort;
  1640. ItemToFocus := nil;
  1641. for Index := 0 to Items.Count - 1 do
  1642. begin
  1643. Item := Items[Index];
  1644. FileName := ItemFileName(Item);
  1645. if FileName = OldItemFocused then
  1646. ItemToFocus := Item;
  1647. if OldSelection.Find(FileName, FoundIndex) then
  1648. Item.Selected := True;
  1649. if CacheIcons and (ItemImageIndex(Item, True) < 0) then
  1650. begin
  1651. FoundIndex := IconCache.IndexOf(FileName);
  1652. if FoundIndex >= 0 then
  1653. SetItemImageIndex(Item, Integer(IconCache.Objects[FoundIndex]));
  1654. end;
  1655. end;
  1656. finally
  1657. Items.EndUpdate;
  1658. OldSelection.Free;
  1659. if CacheIcons then IconCache.Free;
  1660. end;
  1661. // This is below Items.EndUpdate(), to make Scroll() work properly
  1662. if Assigned(ItemToFocus) then
  1663. begin
  1664. // we have found item that was previously focused and visible, scroll to it
  1665. if (ViewStyle = vsReport) and OldFocusedShown and
  1666. (ItemToFocus.Index > OldShownItemOffset) then
  1667. begin
  1668. P := Items[ItemToFocus.Index - OldShownItemOffset].GetPosition;
  1669. // GetPosition is shifted bit low below actual row top.
  1670. // Scroll to the GetPosition would scroll one line lower.
  1671. Scroll(0, P.Y - Items[0].GetPosition.Y);
  1672. end;
  1673. FocusItem(ItemToFocus);
  1674. end;
  1675. // could not scroll when focus is not visible because
  1676. // of previous hack-implementation of FocusItem()
  1677. // - no longer true, this can be re-enabled after some testing
  1678. {$IF False}
  1679. // previously focus item was not visible, scroll to the same position
  1680. // as before
  1681. if (ViewStyle = vsReport) and (not OldFocusedShown) and
  1682. (OldShownItemOffset >= 0) and (Items.Count > 0) then
  1683. begin
  1684. if OldShownItemOffset < Items.Count - VisibleRowCount then
  1685. Scroll(0, OldShownItemOffset)
  1686. else
  1687. Items.Item[Items.Count - 1].MakeVisible(false);
  1688. end
  1689. // do not know where to scroll to, so scroll to focus
  1690. // (or we have tried to scroll to previously focused and visible item,
  1691. // now make sute that it is really visible)
  1692. else {$IFEND}
  1693. if Assigned(ItemToFocus) then ItemToFocus.MakeVisible(false);
  1694. FocusSomething;
  1695. end;
  1696. end;
  1697. procedure TCustomDirView.Load(DoFocusSomething: Boolean);
  1698. var
  1699. SaveCursor: TCursor;
  1700. Delimiters: string;
  1701. LastDirName: string;
  1702. begin
  1703. if not FLoadEnabled or Loading then
  1704. begin
  1705. FDirty := True;
  1706. FAbortLoading := True;
  1707. end
  1708. else
  1709. begin
  1710. FLoading := True;
  1711. try
  1712. FHasParentDir := False;
  1713. if Assigned(FOnStartLoading) then FOnStartLoading(Self);
  1714. SaveCursor := Screen.Cursor;
  1715. Screen.Cursor := crHourGlass;
  1716. try
  1717. FNotifyEnabled := False;
  1718. ClearItems;
  1719. FFilesSize := 0;
  1720. FFilesSelSize := 0;
  1721. SortType := stNone;
  1722. Items.BeginUpdate;
  1723. try
  1724. try
  1725. DoAnimation(True);
  1726. LoadFiles;
  1727. finally
  1728. DoAnimation(False);
  1729. end;
  1730. finally
  1731. Items.EndUpdate;
  1732. end;
  1733. finally
  1734. Screen.Cursor := SaveCursor;
  1735. end;
  1736. finally
  1737. FLoading := False;
  1738. try
  1739. if FAbortLoading then
  1740. begin
  1741. FAbortLoading := False;
  1742. Reload(False);
  1743. end
  1744. else
  1745. begin
  1746. if DirOK then SortItems;
  1747. FAbortLoading := False;
  1748. FDirty := False;
  1749. if (Length(LastPath) > Length(PathName)) and
  1750. (Copy(LastPath, 1, Length(PathName)) = PathName) and
  1751. (Items.Count > 0) then
  1752. begin
  1753. LastDirName := Copy(LastPath, Length(PathName) + 1, MaxInt);
  1754. Delimiters := '\:/';
  1755. if IsDelimiter(Delimiters, LastDirName, 1) then
  1756. begin
  1757. LastDirName := Copy(LastDirName, 2, MaxInt);
  1758. end;
  1759. if LastDelimiter('\:/', LastDirName) = 0 then
  1760. begin
  1761. ItemFocused := FindFileItem(LastDirName);
  1762. end;
  1763. end;
  1764. end;
  1765. finally
  1766. // nested try .. finally block is included
  1767. // because we really want these to be executed
  1768. FNotifyEnabled := True;
  1769. if DoFocusSomething then
  1770. begin
  1771. FocusSomething;
  1772. end;
  1773. if Assigned(FOnLoaded) then
  1774. begin
  1775. FOnLoaded(Self);
  1776. end;
  1777. UpdatePathLabel;
  1778. UpdateStatusBar;
  1779. end;
  1780. end;
  1781. end;
  1782. end;
  1783. procedure TCustomDirView.SetLoadEnabled(Enabled: Boolean);
  1784. begin
  1785. if Enabled <> LoadEnabled then
  1786. begin
  1787. FLoadEnabled := Enabled;
  1788. if Enabled and Dirty then Reload(True);
  1789. end;
  1790. end;
  1791. function TCustomDirView.GetFilesCount: Integer;
  1792. begin
  1793. Result := Items.Count;
  1794. if (Result > 0) and HasParentDir then Dec(Result);
  1795. end;
  1796. procedure TCustomDirView.SetViewStyle(Value: TViewStyle);
  1797. begin
  1798. if (Value <> ViewStyle) and (not FLoading) then
  1799. begin
  1800. FNotifyEnabled := False;
  1801. inherited;
  1802. FNotifyEnabled := True;
  1803. // this is workaround for bug in TCustomNortonLikeListView
  1804. // that clears Items on recreating wnd (caused by change to ViewStyle)
  1805. Reload(True);
  1806. end;
  1807. end;
  1808. procedure TCustomDirView.ColClick(Column: TListColumn);
  1809. var
  1810. ScrollToFocused: Boolean;
  1811. begin
  1812. ScrollToFocused := Assigned(ItemFocused);
  1813. inherited;
  1814. if ScrollToFocused and Assigned(ItemFocused) then
  1815. ItemFocused.MakeVisible(False);
  1816. end;
  1817. procedure TCustomDirView.CustomSortItems(SortProc: Pointer);
  1818. var
  1819. SavedCursor: TCursor;
  1820. SavedNotifyEnabled: Boolean;
  1821. begin
  1822. if HandleAllocated then
  1823. begin
  1824. SavedNotifyEnabled := FNotifyEnabled;
  1825. SavedCursor := Screen.Cursor;
  1826. Items.BeginUpdate;
  1827. try
  1828. Screen.Cursor := crHourglass;
  1829. FNotifyEnabled := False;
  1830. CustomSort(TLVCompare(SortProc), Integer(Pointer(Self)));
  1831. finally
  1832. Screen.Cursor := SavedCursor;
  1833. FNotifyEnabled := SavedNotifyEnabled;
  1834. Items.EndUpdate;
  1835. ItemsReordered;
  1836. end;
  1837. end;
  1838. end;
  1839. procedure TCustomDirView.ReloadForce(CacheIcons: Boolean);
  1840. begin
  1841. FLoadEnabled := True;
  1842. FDirty := False;
  1843. Reload(CacheIcons);
  1844. end;
  1845. procedure TCustomDirView.ScrollOnDragOverBeforeUpdate(ObjectToValidate: TObject);
  1846. begin
  1847. GlobalDragImageList.HideDragImage;
  1848. end;
  1849. procedure TCustomDirView.ScrollOnDragOverAfterUpdate;
  1850. begin
  1851. GlobalDragImageList.ShowDragImage;
  1852. end;
  1853. procedure TCustomDirView.DDDragEnter(DataObj: IDataObject; grfKeyState: Longint;
  1854. Point: TPoint; var dwEffect: longint; var Accept: Boolean);
  1855. var
  1856. Index: Integer;
  1857. begin
  1858. Accept := Accept and DirOK and (not Loading);
  1859. if Accept and
  1860. (DragDropFilesEx.FileList.Count > 0) and
  1861. (Length(TFDDListItem(DragDropFilesEx.FileList[0]^).Name) > 0) and
  1862. (not IsRecycleBin or not DragDropFilesEx.FileNamesAreMapped) then
  1863. begin
  1864. FDragDrive := DriveInfo.GetDriveKey(TFDDListItem(DragDropFilesEx.FileList[0]^).Name);
  1865. FExeDrag := FDDLinkOnExeDrag and
  1866. (deLink in DragDropFilesEx.TargetEffects) and
  1867. ((DragDropFilesEx.AvailableDropEffects and DropEffect_Link) <> 0);
  1868. if FExeDrag then
  1869. begin
  1870. for Index := 0 to DragDropFilesEx.FileList.Count - 1 do
  1871. if not IsExecutable(TFDDListItem(DragDropFilesEx.FileList[Index]^).Name) then
  1872. begin
  1873. FExeDrag := False;
  1874. Break;
  1875. end;
  1876. end;
  1877. end
  1878. else
  1879. begin
  1880. FDragDrive := '';
  1881. end;
  1882. FScrollOnDragOver.StartDrag;
  1883. if Assigned(FOnDDDragEnter) then
  1884. FOnDDDragEnter(Self, DataObj, grfKeyState, Point, dwEffect, Accept);
  1885. end;
  1886. procedure TCustomDirView.DDDragLeave;
  1887. begin
  1888. if Assigned(DropTarget) then
  1889. begin
  1890. if GlobalDragImageList.Dragging then
  1891. GlobalDragImageList.HideDragImage;
  1892. DropTarget := nil;
  1893. Update; {ie30}
  1894. end
  1895. else DropTarget := nil;
  1896. if Assigned(FOnDDDragLeave) then
  1897. FOnDDDragLeave(Self);
  1898. end;
  1899. procedure TCustomDirView.DDDragOver(grfKeyState: Integer; Point: TPoint;
  1900. var dwEffect: Integer);
  1901. var
  1902. DropItem: TListItem;
  1903. CanDrop: Boolean;
  1904. HasDropHandler: Boolean;
  1905. begin
  1906. FDDOwnerIsSource := DragDropFilesEx.OwnerIsSource;
  1907. {Set droptarget if target is directory:}
  1908. if not Loading then DropItem := GetItemAt(Point.X, Point.Y)
  1909. else DropItem := nil;
  1910. HasDropHandler := (Assigned(DropItem) and (not IsRecycleBin) and
  1911. TargetHasDropHandler(DropItem, dwEffect));
  1912. CanDrop := Assigned(DropItem) and (not IsRecycleBin) and
  1913. (ItemIsDirectory(DropItem) or HasDropHandler);
  1914. if (CanDrop and (DropTarget <> DropItem)) or
  1915. (not CanDrop and Assigned(DropTarget)) then
  1916. begin
  1917. if GlobalDragImageList.Dragging then
  1918. begin
  1919. GlobalDragImageList.HideDragImage;
  1920. DropTarget := nil;
  1921. Update;
  1922. if CanDrop then
  1923. begin
  1924. DropTarget := DropItem;
  1925. Update;
  1926. end;
  1927. GlobalDragImageList.ShowDragImage;
  1928. end
  1929. else
  1930. begin
  1931. DropTarget := nil;
  1932. if CanDrop then DropTarget := DropItem;
  1933. end;
  1934. end;
  1935. if not Loading then
  1936. FScrollOnDragOver.DragOver(Point);
  1937. {Set dropeffect:}
  1938. if (not HasDropHandler) and (not Loading) then
  1939. begin
  1940. DDChooseEffect(grfKeyState, dwEffect);
  1941. if Assigned(FOnDDDragOver) then
  1942. FOnDDDragOver(Self, grfKeyState, Point, dwEffect);
  1943. // cannot drop to dragged files
  1944. if DragDropFilesEx.OwnerIsSource and Assigned(DropItem) then
  1945. begin
  1946. if Assigned(ItemFocused) and (not ItemFocused.Selected) then
  1947. begin
  1948. if DropItem = ItemFocused then
  1949. begin
  1950. dwEffect := DropEffect_None;
  1951. end;
  1952. end
  1953. else
  1954. if DropItem.Selected then
  1955. begin
  1956. dwEffect := DropEffect_None;
  1957. end;
  1958. end;
  1959. if DragDropFilesEx.OwnerIsSource and (dwEffect = DropEffect_Move) and
  1960. (not Assigned(DropTarget)) then
  1961. begin
  1962. dwEffect := DropEffect_None;
  1963. end
  1964. else
  1965. if Assigned(DropTarget) and ItemIsRecycleBin(DropTarget) and
  1966. (dwEffect <> DropEffect_None) then
  1967. begin
  1968. dwEffect := DropEffect_Move;
  1969. end;
  1970. end;
  1971. end;
  1972. function TCustomDirView.CustomCreateFileList(Focused, OnlyFocused: Boolean;
  1973. FullPath: Boolean; FileList: TStrings; ItemObject: Boolean): TStrings;
  1974. procedure AddItem(Item: TListItem);
  1975. var
  1976. AObject: TObject;
  1977. begin
  1978. Assert(Assigned(Item));
  1979. if ItemObject then AObject := Item
  1980. else AObject := Item.Data;
  1981. if FullPath then Result.AddObject(ItemFullFileName(Item), AObject)
  1982. else Result.AddObject(ItemFileName(Item), AObject);
  1983. end;
  1984. var
  1985. Item: TListItem;
  1986. begin
  1987. if Assigned(FileList) then Result := FileList
  1988. else Result := TStringList.Create;
  1989. try
  1990. if Assigned(ItemFocused) and
  1991. ((Focused and (not ItemFocused.Selected)) or (SelCount = 0) or OnlyFocused) then
  1992. begin
  1993. AddItem(ItemFocused)
  1994. end
  1995. else
  1996. begin
  1997. Item := GetNextItem(nil, sdAll, [isSelected]);
  1998. while Assigned(Item) do
  1999. begin
  2000. AddItem(Item);
  2001. Item := GetNextItem(Item, sdAll, [isSelected]);
  2002. end;
  2003. end;
  2004. except
  2005. if not Assigned(FileList) then FreeAndNil(Result);
  2006. raise;
  2007. end;
  2008. end;
  2009. function TCustomDirView.CreateFocusedFileList(FullPath: Boolean; FileList: TStrings): TStrings;
  2010. begin
  2011. Result := CustomCreateFileList(False, True, FullPath, FileList);
  2012. end;
  2013. function TCustomDirView.CreateFileList(Focused: Boolean; FullPath: Boolean;
  2014. FileList: TStrings): TStrings;
  2015. begin
  2016. Result := CustomCreateFileList(Focused, False, FullPath, FileList);
  2017. end;
  2018. procedure TCustomDirView.DDDrop(DataObj: IDataObject; grfKeyState: Integer;
  2019. Point: TPoint; var dwEffect: Integer);
  2020. begin
  2021. if GlobalDragImageList.Dragging then
  2022. GlobalDragImageList.HideDragImage;
  2023. if dwEffect = DropEffect_None then
  2024. DropTarget := nil;
  2025. if Assigned(OnDDDrop) then
  2026. OnDDDrop(Self, DataObj, grfKeyState, Point, dwEffect);
  2027. end;
  2028. procedure TCustomDirView.DDQueryContinueDrag(FEscapePressed: LongBool;
  2029. grfKeyState: Integer; var Result: HResult);
  2030. var
  2031. MousePos: TPoint;
  2032. KnowTime: TFileTime;
  2033. begin
  2034. // this method cannot throw exceptions, if it does d&d will not be possible
  2035. // anymore (see TDragDrop.ExecuteOperation, global GInternalSource)
  2036. if Result = DRAGDROP_S_DROP then
  2037. begin
  2038. GetSystemTimeAsFileTime(KnowTime);
  2039. if ((Int64(KnowTime) - Int64(FDragStartTime)) <= DDDragStartDelay) then
  2040. Result := DRAGDROP_S_CANCEL;
  2041. end;
  2042. if Assigned(OnDDQueryContinueDrag) then
  2043. begin
  2044. OnDDQueryContinueDrag(Self, FEscapePressed, grfKeyState, Result);
  2045. end;
  2046. try
  2047. if FEscapePressed then
  2048. begin
  2049. if GlobalDragImageList.Dragging then
  2050. GlobalDragImageList.HideDragImage;
  2051. end
  2052. else
  2053. begin
  2054. if GlobalDragImageList.Dragging Then
  2055. begin
  2056. MousePos := ParentForm.ScreenToClient(Mouse.CursorPos);
  2057. {Move the drag image to the new position and show it:}
  2058. if (MousePos.X <> FDragPos.X) or (MousePos.Y <> FDragPos.Y) then
  2059. begin
  2060. FDragPos := MousePos;
  2061. if PtInRect(ParentForm.BoundsRect, Mouse.CursorPos) then
  2062. begin
  2063. GlobalDragImageList.DragMove(MousePos.X, MousePos.Y);
  2064. GlobalDragImageList.ShowDragImage;
  2065. end
  2066. else GlobalDragImageList.HideDragImage;
  2067. end;
  2068. end;
  2069. end;
  2070. except
  2071. // do not care if the above fails
  2072. // (Mouse.CursorPos fails when desktop is locked by user)
  2073. end;
  2074. end;
  2075. procedure TCustomDirView.DDSpecifyDropTarget(Sender: TObject;
  2076. DragDropHandler: Boolean; Point: TPoint; var pidlFQ: PItemIDList;
  2077. var Filename: string);
  2078. var
  2079. Item: TListItem;
  2080. begin
  2081. pidlFQ := nil;
  2082. if DirOK and (not Loading) then
  2083. begin
  2084. if DragDropHandler then
  2085. begin
  2086. if Assigned(DropTarget) and ItemIsDirectory(DropTarget) then
  2087. FileName := ItemFullFileName(DropTarget)
  2088. else
  2089. FileName := PathName;
  2090. end
  2091. else
  2092. begin
  2093. Item := GetItemAt(Point.X, Point.Y);
  2094. if Assigned(Item) and (not ItemIsDirectory(Item)) and
  2095. (not IsRecycleBin) then
  2096. FileName := ItemFullFileName(Item)
  2097. else
  2098. FileName := '';
  2099. end;
  2100. end
  2101. else FileName := '';
  2102. end;
  2103. procedure TCustomDirView.DDMenuPopup(Sender: TObject; AMenu: HMenu;
  2104. DataObj: IDataObject; AMinCustCmd: Integer; grfKeyState: Longint; pt: TPoint);
  2105. begin
  2106. if Assigned(OnDDMenuPopup) then
  2107. begin
  2108. OnDDMenuPopup(Self, AMenu, DataObj, AMinCustCmd, grfKeyState, pt);
  2109. end;
  2110. end;
  2111. procedure TCustomDirView.DDMenuDone(Sender: TObject; AMenu: HMenu);
  2112. begin
  2113. end;
  2114. procedure TCustomDirView.DDDropHandlerSucceeded(Sender: TObject;
  2115. grfKeyState: Integer; Point: TPoint; dwEffect: Integer);
  2116. begin
  2117. DropTarget := nil;
  2118. end;
  2119. procedure TCustomDirView.DDChooseEffect(grfKeyState: Integer; var dwEffect: Integer);
  2120. begin
  2121. if Assigned(FOnDDChooseEffect) then
  2122. FOnDDChooseEffect(Self, grfKeyState, dwEffect);
  2123. end;
  2124. procedure TCustomDirView.DDGiveFeedback(dwEffect: Integer;
  2125. var Result: HResult);
  2126. begin
  2127. if Assigned(FOnDDGiveFeedback) then
  2128. FOnDDGiveFeedback(Self, dwEffect, Result);
  2129. end;
  2130. procedure TCustomDirView.DDProcessDropped(Sender: TObject;
  2131. grfKeyState: Integer; Point: TPoint; dwEffect: Integer);
  2132. begin
  2133. if DirOK and (not Loading) then
  2134. try
  2135. try
  2136. if Assigned(FOnDDProcessDropped) then
  2137. FOnDDProcessDropped(Self, grfKeyState, Point, dwEffect);
  2138. if dwEffect <> DropEffect_None then
  2139. begin
  2140. PerformItemDragDropOperation(DropTarget, dwEffect);
  2141. if Assigned(FOnDDExecuted) then
  2142. FOnDDExecuted(Self, dwEffect);
  2143. end;
  2144. finally
  2145. DragDropFilesEx.FileList.Clear;
  2146. DropTarget := nil;
  2147. end;
  2148. except
  2149. Application.HandleException(Self);
  2150. end;
  2151. end;
  2152. function TCustomDirView.AnyFileSelected(
  2153. OnlyFocused: Boolean; FilesOnly: Boolean; FocusedFileOnlyWhenFocused: Boolean): Boolean;
  2154. var
  2155. Item: TListItem;
  2156. begin
  2157. if OnlyFocused or
  2158. ((SelCount = 0) and
  2159. ((not FocusedFileOnlyWhenFocused) or
  2160. (Focused and (GetParentForm(Self).Handle = GetForegroundWindow())))) then
  2161. begin
  2162. Result := Assigned(ItemFocused) and ItemIsFile(ItemFocused) and
  2163. ((not FilesOnly) or (not ItemIsDirectory(ItemFocused)));
  2164. end
  2165. else
  2166. begin
  2167. Result := True;
  2168. Item := GetNextItem(nil, sdAll, [isSelected]);
  2169. while Assigned(Item) do
  2170. begin
  2171. if ItemIsFile(Item) and
  2172. ((not FilesOnly) or (not ItemIsDirectory(Item))) then Exit;
  2173. Item := GetNextItem(Item, sdAll, [isSelected]);
  2174. end;
  2175. Result := False;
  2176. end;
  2177. end;
  2178. function TCustomDirView.CanEdit(Item: TListItem): Boolean;
  2179. begin
  2180. Result :=
  2181. (inherited CanEdit(Item) or FForceRename) and (not Loading) and
  2182. Assigned(Item) and (not ReadOnly) and (not IsRecycleBin) and
  2183. (not ItemIsParentDirectory(Item));
  2184. if Result then FLoadEnabled := False;
  2185. FForceRename := False;
  2186. end;
  2187. function TCustomDirView.CanChangeSelection(Item: TListItem;
  2188. Select: Boolean): Boolean;
  2189. begin
  2190. Result :=
  2191. (not Loading) and
  2192. not (Assigned(Item) and Assigned(Item.Data) and
  2193. ItemIsParentDirectory(Item));
  2194. end;
  2195. procedure TCustomDirView.Edit(const HItem: TLVItem);
  2196. var
  2197. Info: string;
  2198. Index: Integer;
  2199. begin
  2200. // When rename is confirmed by clicking outside of the edit box, and the actual rename operation
  2201. // displays error message or simply pumps a message queue (like during lenghty remote directory reload),
  2202. // drag mouse selection start. It posssibly happens only on the remote panel due to it being completelly reloaded.
  2203. ReleaseCapture;
  2204. if Length(HItem.pszText) = 0 then LoadEnabled := True
  2205. else
  2206. begin
  2207. {Does the changed filename contains invalid characters?}
  2208. if StrContains(FInvalidNameChars, HItem.pszText) then
  2209. begin
  2210. Info := FInvalidNameChars;
  2211. for Index := Length(Info) downto 1 do
  2212. System.Insert(Space, Info, Index);
  2213. MessageBeep(MB_ICONHAND);
  2214. if MessageDlg(SErrorInvalidName + Space + Info, mtError,
  2215. [mbOK, mbAbort], 0) = mrOK then RetryRename(HItem.pszText);
  2216. LoadEnabled := True;
  2217. end
  2218. else
  2219. begin
  2220. InternalEdit(HItem);
  2221. end;
  2222. end;
  2223. end; {Edit}
  2224. procedure TCustomDirView.EndSelectionUpdate;
  2225. begin
  2226. inherited;
  2227. if FUpdatingSelection = 0 then
  2228. UpdateStatusBar;
  2229. end; { EndUpdatingSelection }
  2230. procedure TCustomDirView.ExecuteCurrentFile;
  2231. begin
  2232. Assert(Assigned(ItemFocused));
  2233. Execute(ItemFocused);
  2234. end;
  2235. procedure TCustomDirView.Execute(Item: TListItem);
  2236. var
  2237. AllowExec: Boolean;
  2238. begin
  2239. Assert(Assigned(Item));
  2240. if Assigned(Item) and Assigned(Item.Data) and (not Loading) then
  2241. begin
  2242. if IsRecycleBin and (not ItemIsParentDirectory(Item)) then DisplayPropertiesMenu
  2243. else
  2244. begin
  2245. AllowExec := True;
  2246. if Assigned(FOnExecFile) then FOnExecFile(Self, Item, AllowExec);
  2247. if AllowExec then
  2248. begin
  2249. if ItemIsParentDirectory(Item) then ExecuteParentDirectory
  2250. else ExecuteFile(Item);
  2251. end;
  2252. end;
  2253. end;
  2254. end;
  2255. procedure TCustomDirView.GetDisplayInfo(ListItem: TListItem;
  2256. var DispInfo: TLVItem);
  2257. begin
  2258. // Nothing
  2259. end;
  2260. procedure TCustomDirView.WMUserRename(var Message: TMessage);
  2261. begin
  2262. if Assigned(ItemFocused) then
  2263. begin
  2264. FForceRename := True;
  2265. ListView_EditLabel(Handle, ItemFocused.Index);
  2266. SetWindowText(ListView_GetEditControl(Self.Handle),
  2267. PChar(FLastRenameName));
  2268. end;
  2269. end;
  2270. procedure TCustomDirView.RetryRename(NewName: string);
  2271. begin
  2272. FLastRenameName := NewName;
  2273. PostMessage(Self.Handle, WM_USER_RENAME, Longint(PChar(NewName)), 0);
  2274. end;
  2275. procedure TCustomDirView.AddToDragFileList(FileList: TFileList; Item: TListItem);
  2276. begin
  2277. FileList.AddItem(nil, ItemFullFileName(Item));
  2278. end;
  2279. procedure TCustomDirView.DDDragDetect(grfKeyState: Integer; DetectStart,
  2280. Point: TPoint; DragStatus: TDragDetectStatus);
  2281. var
  2282. FilesCount: Integer;
  2283. DirsCount: Integer;
  2284. Item: TListItem;
  2285. FirstItem : TListItem;
  2286. Bitmap: TBitmap;
  2287. ImageListHandle: HImageList;
  2288. Spot: TPoint;
  2289. ItemPos: TPoint;
  2290. DragText: string;
  2291. ClientPoint: TPoint;
  2292. FileListCreated: Boolean;
  2293. AvoidDragImage: Boolean;
  2294. DataObject: TDataObject;
  2295. begin
  2296. if Assigned(FOnDDDragDetect) then
  2297. FOnDDDragDetect(Self, grfKeyState, DetectStart, Point, DragStatus);
  2298. FLastDDResult := drCancelled;
  2299. if (DragStatus = ddsDrag) and (not Loading) and (MarkedCount > 0) then
  2300. begin
  2301. DragDropFilesEx.CompleteFileList := DragCompleteFileList;
  2302. DragDropFilesEx.FileList.Clear;
  2303. FirstItem := nil;
  2304. FilesCount := 0;
  2305. DirsCount := 0;
  2306. FileListCreated := False;
  2307. AvoidDragImage := False;
  2308. if Assigned(OnDDCreateDragFileList) then
  2309. begin
  2310. OnDDCreateDragFileList(Self, DragDropFilesEx.FileList, FileListCreated);
  2311. if FileListCreated then
  2312. begin
  2313. AvoidDragImage := True;
  2314. end;
  2315. end;
  2316. if not FileListCreated then
  2317. begin
  2318. if Assigned(ItemFocused) and (not ItemFocused.Selected) then
  2319. begin
  2320. if ItemCanDrag(ItemFocused) then
  2321. begin
  2322. FirstItem := ItemFocused;
  2323. AddToDragFileList(DragDropFilesEx.FileList, ItemFocused);
  2324. if ItemIsDirectory(ItemFocused) then Inc(DirsCount)
  2325. else Inc(FilesCount);
  2326. end;
  2327. end
  2328. else
  2329. if SelCount > 0 then
  2330. begin
  2331. Item := GetNextItem(nil, sdAll, [isSelected]);
  2332. while Assigned(Item) do
  2333. begin
  2334. if ItemCanDrag(Item) then
  2335. begin
  2336. if not Assigned(FirstItem) then FirstItem := Item;
  2337. AddToDragFileList(DragDropFilesEx.FileList, Item);
  2338. if ItemIsDirectory(Item) then Inc(DirsCount)
  2339. else Inc(FilesCount);
  2340. end;
  2341. Item := GetNextItem(Item, sdAll, [isSelected]);
  2342. end;
  2343. end;
  2344. end;
  2345. if DragDropFilesEx.FileList.Count > 0 then
  2346. begin
  2347. FDragEnabled := False;
  2348. {Create the dragimage:}
  2349. GlobalDragImageList := DragImageList;
  2350. // This code is not used anymore
  2351. if UseDragImages and (not AvoidDragImage) then
  2352. begin
  2353. ImageListHandle := ListView_CreateDragImage(Handle, FirstItem.Index, Spot);
  2354. ItemPos := ClientToScreen(FirstItem.DisplayRect(drBounds).TopLeft);
  2355. if ImageListHandle <> Invalid_Handle_Value then
  2356. begin
  2357. GlobalDragImageList.Handle := ImageListHandle;
  2358. if FilesCount + DirsCount = 1 then
  2359. begin
  2360. ItemPos := ClientToScreen(FirstItem.DisplayRect(drBounds).TopLeft);
  2361. GlobalDragImageList.SetDragImage(0,
  2362. DetectStart.X - ItemPos.X, DetectStart.Y - ItemPos.Y);
  2363. end
  2364. else
  2365. begin
  2366. GlobalDragImageList.Clear;
  2367. GlobalDragImageList.Width := 32;
  2368. GlobalDragImageList.Height := 32;
  2369. if GlobalDragImageList.GetResource(rtBitMap, 'DRAGFILES', 0,
  2370. [lrTransparent], $FFFFFF) Then
  2371. begin
  2372. Bitmap := TBitmap.Create;
  2373. try
  2374. try
  2375. GlobalDragImageList.GetBitmap(0, Bitmap);
  2376. Bitmap.Canvas.Font.Assign(Self.Font);
  2377. DragText := '';
  2378. if FilesCount > 0 then
  2379. DragText := Format(STextFiles, [FilesCount]);
  2380. if DirsCount > 0 then
  2381. begin
  2382. if FilesCount > 0 then
  2383. DragText := DragText + ', ';
  2384. DragText := DragText + Format(STextDirectories, [DirsCount]);
  2385. end;
  2386. Bitmap.Width := 33 + Bitmap.Canvas.TextWidth(DragText);
  2387. Bitmap.TransparentMode := tmAuto;
  2388. Bitmap.Canvas.TextOut(33,
  2389. Max(24 - Abs(Canvas.Font.Height), 0), DragText);
  2390. GlobalDragImageList.Clear;
  2391. GlobalDragImageList.Width := Bitmap.Width;
  2392. GlobalDragImageList.AddMasked(Bitmap,
  2393. Bitmap.Canvas.Pixels[0, 0]);
  2394. GlobalDragImageList.SetDragImage(0, 25, 20);
  2395. except
  2396. if GlobalDragImageList.GetResource(rtBitMap, 'DRAGFILES',
  2397. 0, [lrTransparent], $FFFFFF) then
  2398. GlobalDragImageList.SetDragImage(0, 25, 20);
  2399. end;
  2400. finally
  2401. Bitmap.Free;
  2402. end;
  2403. end;
  2404. end;
  2405. ClientPoint := ParentForm.ScreenToClient(Point);
  2406. GlobalDragImageList.BeginDrag(ParentForm.Handle,
  2407. ClientPoint.X, ClientPoint.Y);
  2408. GlobalDragImageList.HideDragImage;
  2409. ShowCursor(True);
  2410. end;
  2411. end;
  2412. FContextMenu := False;
  2413. if IsRecycleBin then DragDropFilesEx.SourceEffects := [deMove]
  2414. else DragDropFilesEx.SourceEffects := DragSourceEffects;
  2415. DropSourceControl := Self;
  2416. try
  2417. GetSystemTimeAsFileTime(FDragStartTime);
  2418. DataObject := nil;
  2419. if Assigned(OnDDCreateDataObject) then
  2420. begin
  2421. OnDDCreateDataObject(Self, DataObject);
  2422. end;
  2423. {Execute the drag&drop-Operation:}
  2424. FLastDDResult := DragDropFilesEx.Execute(DataObject);
  2425. // The drag&drop operation is finished, so clean up the used drag image.
  2426. // This also restores the default mouse cursor
  2427. // (which is set to "none" in GlobalDragImageList.BeginDrag above)
  2428. // But it's actually too late, we would need to do it when mouse button
  2429. // is realesed already. Otherwise the cursor is hidden when hovering over
  2430. // main window, while target application is processing dropped file
  2431. // (particularly when Explorer displays progress window or
  2432. // overwrite confirmation prompt)
  2433. GlobalDragImageList.EndDrag;
  2434. GlobalDragImageList.Clear;
  2435. Application.ProcessMessages;
  2436. finally
  2437. DragDropFilesEx.FileList.Clear;
  2438. FContextMenu := False;
  2439. try
  2440. if Assigned(OnDDEnd) then
  2441. OnDDEnd(Self);
  2442. finally
  2443. DropTarget := nil;
  2444. DropSourceControl := nil;
  2445. end;
  2446. end;
  2447. end;
  2448. end;
  2449. end;
  2450. procedure TCustomDirView.Notification(AComponent: TComponent; Operation: TOperation);
  2451. begin
  2452. inherited;
  2453. if Operation = opRemove then
  2454. begin
  2455. if AComponent = PathLabel then FPathLabel := nil;
  2456. end;
  2457. end; { Notification }
  2458. procedure TCustomDirView.WMAppCommand(var Message: TMessage);
  2459. var
  2460. Command: Integer;
  2461. Shift: TShiftState;
  2462. begin
  2463. Command := HiWord(Message.lParam) and (not FAPPCOMMAND_MASK);
  2464. Shift := KeyDataToShiftState(HiWord(Message.lParam) and FAPPCOMMAND_MASK);
  2465. if Shift * [ssShift, ssAlt, ssCtrl] = [] then
  2466. begin
  2467. if Command = APPCOMMAND_BROWSER_BACKWARD then
  2468. begin
  2469. Message.Result := 1;
  2470. if BackCount >= 1 then DoHistoryGo(-1);
  2471. end
  2472. else
  2473. if Command = APPCOMMAND_BROWSER_FORWARD then
  2474. begin
  2475. Message.Result := 1;
  2476. if ForwardCount >= 1 then DoHistoryGo(1);
  2477. end
  2478. else
  2479. if Command = APPCOMMAND_BROWSER_REFRESH then
  2480. begin
  2481. Message.Result := 1;
  2482. BusyOperation(ReloadDirectory);
  2483. end
  2484. else
  2485. if Command = APPCOMMAND_BROWSER_HOME then
  2486. begin
  2487. Message.Result := 1;
  2488. BusyOperation(ExecuteHomeDirectory);
  2489. end
  2490. else inherited;
  2491. end
  2492. else inherited;
  2493. end;
  2494. procedure TCustomDirView.CMColorChanged(var Message: TMessage);
  2495. begin
  2496. inherited;
  2497. ForceColorChange(Self);
  2498. end;
  2499. function TCustomDirView.FindFileItem(FileName: string): TListItem;
  2500. type
  2501. TFileNameCompare = function(const S1, S2: string): Integer;
  2502. var
  2503. Index: Integer;
  2504. CompareFunc: TFileNameCompare;
  2505. begin
  2506. if FCaseSensitive then CompareFunc := CompareStr
  2507. else CompareFunc := CompareText;
  2508. for Index := 0 to Items.Count - 1 do
  2509. begin
  2510. if CompareFunc(FileName, ItemFileName(Items[Index])) = 0 then
  2511. begin
  2512. Result := Items[Index];
  2513. Exit;
  2514. end;
  2515. end;
  2516. Result := nil;
  2517. end;
  2518. procedure TCustomDirView.DoAnimation(Start: Boolean);
  2519. begin
  2520. if Start and LoadAnimation then
  2521. begin
  2522. if not Assigned(FAnimation) then
  2523. begin
  2524. FAnimation := TAnimate.Create(Self);
  2525. try
  2526. FAnimation.Top := (Height - FAnimation.Height) div 2;
  2527. FAnimation.Left := (Width - FAnimation.Width) div 2;
  2528. FAnimation.Parent := Self;
  2529. FAnimation.CommonAVI := aviFindFolder;
  2530. FAnimation.Transparent := True;
  2531. FAnimation.Active := True;
  2532. except
  2533. FreeAndNil(FAnimation);
  2534. end;
  2535. end;
  2536. end
  2537. else
  2538. if not Start then
  2539. begin
  2540. FreeAndNil(FAnimation);
  2541. end;
  2542. end; { DoAnimation }
  2543. function TCustomDirView.GetForwardCount: Integer;
  2544. begin
  2545. Result := FHistoryPaths.Count - BackCount;
  2546. end; { GetForwardCount }
  2547. procedure TCustomDirView.LimitHistorySize;
  2548. begin
  2549. while FHistoryPaths.Count > MaxHistoryCount do
  2550. begin
  2551. if BackCount > 0 then
  2552. begin
  2553. FHistoryPaths.Delete(0);
  2554. Dec(FBackCount);
  2555. end
  2556. else
  2557. FHistoryPaths.Delete(FHistoryPaths.Count-1);
  2558. end;
  2559. end; { LimitHistorySize }
  2560. function TCustomDirView.GetHistoryPath(Index: Integer): string;
  2561. begin
  2562. Assert(Assigned(FHistoryPaths));
  2563. if Index = 0 then Result := PathName
  2564. else
  2565. if Index < 0 then Result := FHistoryPaths[Index + BackCount]
  2566. else
  2567. if Index > 0 then Result := FHistoryPaths[Index + BackCount - 1];
  2568. end; { GetHistoryPath }
  2569. procedure TCustomDirView.SetMaxHistoryCount(Value: Integer);
  2570. begin
  2571. if FMaxHistoryCount <> Value then
  2572. begin
  2573. FMaxHistoryCount := Value;
  2574. DoHistoryChange;
  2575. end;
  2576. end; { SetMaxHistoryCount }
  2577. procedure TCustomDirView.DoHistoryChange;
  2578. begin
  2579. LimitHistorySize;
  2580. if Assigned(OnHistoryChange) then
  2581. OnHistoryChange(Self);
  2582. end; { DoHistoryChange }
  2583. procedure TCustomDirView.DoHistoryGo(Index: Integer);
  2584. var
  2585. Cancel: Boolean;
  2586. begin
  2587. if StartBusy then
  2588. try
  2589. Cancel := False;
  2590. if Assigned(OnHistoryGo) then
  2591. OnHistoryGo(Self, Index, Cancel);
  2592. if not Cancel then HistoryGo(Index);
  2593. finally
  2594. EndBusy;
  2595. end;
  2596. end;
  2597. procedure TCustomDirView.HistoryGo(Index: Integer);
  2598. var
  2599. PrevPath: string;
  2600. begin
  2601. if Index <> 0 then
  2602. begin
  2603. PrevPath := FHistoryPath;
  2604. FDontRecordPath := True;
  2605. try
  2606. Path := HistoryPath[Index];
  2607. finally
  2608. FDontRecordPath := False;
  2609. end;
  2610. FHistoryPaths.Insert(FBackCount, PrevPath);
  2611. FHistoryPaths.Delete(Index + BackCount);
  2612. Inc(FBackCount, Index);
  2613. DoHistoryChange;
  2614. end;
  2615. end; { HistoryGo }
  2616. procedure TCustomDirView.PathChanging(Relative: Boolean);
  2617. begin
  2618. if Relative then FLastPath := PathName
  2619. else FLastPath := '';
  2620. FSavedNames.Clear;
  2621. end;
  2622. procedure TCustomDirView.PathChanged;
  2623. var
  2624. Index: Integer;
  2625. begin
  2626. if Assigned(OnPathChange) then
  2627. OnPathChange(Self);
  2628. if (not FDontRecordPath) and (FHistoryPath <> '') and (FHistoryPath <> PathName) then
  2629. begin
  2630. Assert(Assigned(FHistoryPaths));
  2631. for Index := FHistoryPaths.Count - 1 downto BackCount do
  2632. FHistoryPaths.Delete(Index);
  2633. FHistoryPaths.Add(FHistoryPath);
  2634. Inc(FBackCount);
  2635. DoHistoryChange;
  2636. end;
  2637. FHistoryPath := PathName;
  2638. end; { PathChanged }
  2639. procedure TCustomDirView.ProcessChangedFiles(DirView: TCustomDirView;
  2640. FileList: TStrings; FullPath: Boolean; ExistingOnly: Boolean;
  2641. Criterias: TCompareCriterias);
  2642. var
  2643. Item, MirrorItem: TListItem;
  2644. FileTime, MirrorFileTime: TDateTime;
  2645. OldCursor: TCursor;
  2646. Index: Integer;
  2647. Changed: Boolean;
  2648. SameTime: Boolean;
  2649. Precision, MirrorPrecision: TDateTimePrecision;
  2650. begin
  2651. Assert(Valid);
  2652. OldCursor := Screen.Cursor;
  2653. if not Assigned(FileList) then
  2654. begin
  2655. Items.BeginUpdate;
  2656. BeginSelectionUpdate;
  2657. end;
  2658. try
  2659. Screen.Cursor := crHourGlass;
  2660. for Index := 0 to Items.Count-1 do
  2661. begin
  2662. Item := Items[Index];
  2663. Changed := False;
  2664. if not ItemIsDirectory(Item) then
  2665. begin
  2666. MirrorItem := DirView.FindFileItem(ItemFileName(Item));
  2667. if MirrorItem = nil then
  2668. begin
  2669. Changed := not ExistingOnly;
  2670. end
  2671. else
  2672. begin
  2673. if ccTime in Criterias then
  2674. begin
  2675. FileTime := ItemFileTime(Item, Precision);
  2676. MirrorFileTime := DirView.ItemFileTime(MirrorItem, MirrorPrecision);
  2677. if MirrorPrecision < Precision then Precision := MirrorPrecision;
  2678. if Precision <> tpMillisecond then
  2679. begin
  2680. ReduceDateTimePrecision(FileTime, Precision);
  2681. ReduceDateTimePrecision(MirrorFileTime, Precision);
  2682. end;
  2683. SameTime := (FileTime = MirrorFileTime);
  2684. if Precision = tpSecond then
  2685. begin
  2686. // 1 ms more solves the rounding issues
  2687. // (see also Common.cpp)
  2688. MirrorFileTime := MirrorFileTime + EncodeTime(0, 0, 1, 1);
  2689. end;
  2690. Changed :=
  2691. (FileTime > MirrorFileTime);
  2692. end
  2693. else
  2694. begin
  2695. SameTime := True;
  2696. end;
  2697. if (not Changed) and SameTime and (ccSize in Criterias) then
  2698. begin
  2699. Changed := ItemFileSize(Item) <> DirView.ItemFileSize(MirrorItem);
  2700. end
  2701. end;
  2702. end;
  2703. if Assigned(FileList) then
  2704. begin
  2705. if Changed then
  2706. begin
  2707. if FullPath then
  2708. begin
  2709. FileList.AddObject(ItemFullFileName(Item), Item.Data)
  2710. end
  2711. else
  2712. begin
  2713. FileList.AddObject(ItemFileName(Item), Item.Data);
  2714. end;
  2715. end;
  2716. end
  2717. else
  2718. begin
  2719. Item.Selected := Changed;
  2720. end;
  2721. end;
  2722. finally
  2723. Screen.Cursor := OldCursor;
  2724. if not Assigned(FileList) then
  2725. begin
  2726. Items.EndUpdate;
  2727. EndSelectionUpdate;
  2728. end;
  2729. end;
  2730. end;
  2731. function TCustomDirView.CreateChangedFileList(DirView: TCustomDirView;
  2732. FullPath: Boolean; ExistingOnly: Boolean; Criterias: TCompareCriterias): TStrings;
  2733. begin
  2734. Result := TStringList.Create;
  2735. try
  2736. ProcessChangedFiles(DirView, Result, FullPath, ExistingOnly, Criterias);
  2737. except
  2738. FreeAndNil(Result);
  2739. raise;
  2740. end;
  2741. end;
  2742. function TCustomDirView.CanPasteFromClipBoard: Boolean;
  2743. begin
  2744. Result := False;
  2745. if DirOK and (Path <> '') and Windows.OpenClipboard(0) then
  2746. begin
  2747. Result := IsClipboardFormatAvailable(CF_HDROP);
  2748. Windows.CloseClipBoard;
  2749. end;
  2750. end; {CanPasteFromClipBoard}
  2751. procedure TCustomDirView.CompareFiles(DirView: TCustomDirView;
  2752. ExistingOnly: Boolean; Criterias: TCompareCriterias);
  2753. begin
  2754. ProcessChangedFiles(DirView, nil, True, ExistingOnly, Criterias);
  2755. end;
  2756. procedure TCustomDirView.FocusSomething;
  2757. begin
  2758. if FSavedSelection then FPendingFocusSomething := True
  2759. else inherited;
  2760. end;
  2761. procedure TCustomDirView.SaveSelection;
  2762. var
  2763. Closest: TListItem;
  2764. begin
  2765. Assert(not FSavedSelection);
  2766. FSavedSelectionFile := '';
  2767. FSavedSelectionLastFile := '';
  2768. if Assigned(ItemFocused) then
  2769. begin
  2770. FSavedSelectionLastFile := ItemFocused.Caption;
  2771. end;
  2772. Closest := ClosestUnselected(ItemFocused);
  2773. if Assigned(Closest) then
  2774. begin
  2775. FSavedSelectionFile := Closest.Caption;
  2776. end;
  2777. FSavedSelection := True;
  2778. end;
  2779. procedure TCustomDirView.RestoreSelection;
  2780. var
  2781. ItemToSelect: TListItem;
  2782. begin
  2783. Assert(FSavedSelection);
  2784. FSavedSelection := False;
  2785. if (FSavedSelectionLastFile <> '') and
  2786. ((not Assigned(ItemFocused)) or
  2787. (ItemFocused.Caption <> FSavedSelectionLastFile)) then
  2788. begin
  2789. ItemToSelect := FindFileItem(FSavedSelectionFile);
  2790. if Assigned(ItemToSelect) then
  2791. begin
  2792. ItemFocused := ItemToSelect;
  2793. end;
  2794. end;
  2795. if not Assigned(ItemFocused) then FocusSomething
  2796. else ItemFocused.MakeVisible(False);
  2797. end;
  2798. procedure TCustomDirView.DiscardSavedSelection;
  2799. begin
  2800. Assert(FSavedSelection);
  2801. FSavedSelection := False;
  2802. if FPendingFocusSomething then
  2803. begin
  2804. FPendingFocusSomething := False;
  2805. FocusSomething;
  2806. end;
  2807. end;
  2808. procedure TCustomDirView.SaveSelectedNames;
  2809. var
  2810. Index: Integer;
  2811. Item: TListItem;
  2812. begin
  2813. FSavedNames.Clear;
  2814. FSavedNames.CaseSensitive := FCaseSensitive;
  2815. if SelCount > 0 then // optimalisation
  2816. begin
  2817. for Index := 0 to Items.Count-1 do
  2818. begin
  2819. Item := Items[Index];
  2820. if Item.Selected then
  2821. FSavedNames.Add(ItemFileName(Item));
  2822. end;
  2823. end;
  2824. // as optimalisation the list is sorted only when the selection is restored
  2825. end;
  2826. procedure TCustomDirView.RestoreSelectedNames;
  2827. var
  2828. Index, FoundIndex: Integer;
  2829. Item: TListItem;
  2830. begin
  2831. FSavedNames.Sort;
  2832. for Index := 0 to Items.Count - 1 do
  2833. begin
  2834. Item := Items[Index];
  2835. Item.Selected := FSavedNames.Find(ItemFileName(Item), FoundIndex);
  2836. end;
  2837. end;
  2838. function TCustomDirView.GetSelectedNamesSaved: Boolean;
  2839. begin
  2840. Result := (FSavedNames.Count > 0);
  2841. end;
  2842. procedure TCustomDirView.ContinueSession(Continue: Boolean);
  2843. begin
  2844. if Continue then FLastPath := PathName
  2845. else FLastPath := '';
  2846. end;
  2847. function TCustomDirView.SaveState: TObject;
  2848. var
  2849. State: TDirViewState;
  2850. DirColProperties: TCustomDirViewColProperties;
  2851. begin
  2852. State := TDirViewState.Create;
  2853. State.HistoryPaths := TStringList.Create;
  2854. State.HistoryPaths.Assign(FHistoryPaths);
  2855. State.BackCount := FBackCount;
  2856. // TCustomDirViewColProperties should not be here
  2857. DirColProperties := ColProperties as TCustomDirViewColProperties;
  2858. Assert(Assigned(DirColProperties));
  2859. State.SortStr := DirColProperties.SortStr;
  2860. State.Mask := Mask;
  2861. if Assigned(ItemFocused) then State.FocusedItem := ItemFocused.Caption
  2862. else State.FocusedItem := '';
  2863. Result := State;
  2864. end;
  2865. procedure TCustomDirView.RestoreState(AState: TObject);
  2866. var
  2867. State: TDirViewState;
  2868. DirColProperties: TCustomDirViewColProperties;
  2869. ListItem: TListItem;
  2870. begin
  2871. Assert(AState is TDirViewState);
  2872. State := AState as TDirViewState;
  2873. Assert(Assigned(State));
  2874. FHistoryPaths.Assign(State.HistoryPaths);
  2875. FBackCount := State.BackCount;
  2876. DoHistoryChange;
  2877. // TCustomDirViewColProperties should not be here
  2878. DirColProperties := ColProperties as TCustomDirViewColProperties;
  2879. Assert(Assigned(DirColProperties));
  2880. DirColProperties.SortStr := State.SortStr;
  2881. Mask := State.Mask;
  2882. if State.FocusedItem <> '' then
  2883. begin
  2884. ListItem := FindFileItem(State.FocusedItem);
  2885. if Assigned(ListItem) then
  2886. begin
  2887. ItemFocused := ListItem;
  2888. ListItem.MakeVisible(False);
  2889. end;
  2890. end;
  2891. end;
  2892. procedure TCustomDirView.ClearState;
  2893. begin
  2894. FHistoryPaths.Clear;
  2895. FBackCount := 0;
  2896. DoHistoryChange;
  2897. end;
  2898. procedure TCustomDirView.SetMask(Value: string);
  2899. begin
  2900. if Mask <> Value then
  2901. begin
  2902. FMask := Value;
  2903. UpdatePathLabel;
  2904. if DirOK then Reload(False);
  2905. end;
  2906. end;{SetMask}
  2907. procedure TCustomDirView.SetNaturalOrderNumericalSorting(Value: Boolean);
  2908. begin
  2909. if NaturalOrderNumericalSorting <> Value then
  2910. begin
  2911. FNaturalOrderNumericalSorting := Value;
  2912. SortItems;
  2913. end;
  2914. end;
  2915. // WM_SETFOCUS works even when focus is moved to another window/app,
  2916. // while .Enter works only when focus is moved to order control of the same window.
  2917. procedure TCustomDirView.WMSetFocus(var Message: TWMSetFocus);
  2918. begin
  2919. inherited;
  2920. EnsureSelectionRedrawn;
  2921. UpdatePathLabel;
  2922. end;
  2923. procedure TCustomDirView.WMKillFocus(var Message: TWMKillFocus);
  2924. begin
  2925. inherited;
  2926. EnsureSelectionRedrawn;
  2927. UpdatePathLabel;
  2928. end;
  2929. procedure TCustomDirView.EnsureSelectionRedrawn;
  2930. begin
  2931. // WORKAROUND
  2932. // when receiving/losing focus, selection is not redrawn in report view
  2933. // (except for focus item selection),
  2934. // probably when double buffering is enabled (LVS_EX_DOUBLEBUFFER).
  2935. // But even without LVS_EX_DOUBLEBUFFER, selection behind file icon is not updated.
  2936. if ViewStyle = vsReport then
  2937. begin
  2938. if (SelCount >= 2) or ((SelCount >= 1) and ((not Assigned(ItemFocused)) or (not ItemFocused.Selected))) then
  2939. begin
  2940. Invalidate;
  2941. end
  2942. else
  2943. if Assigned(ItemFocused) and ItemFocused.Selected then
  2944. begin
  2945. // Optimization. When no item is selected, redraw just the focused item.
  2946. ItemFocused.Update;
  2947. end;
  2948. end;
  2949. end;
  2950. function TCustomDirView.DoBusy(Busy: Integer): Boolean;
  2951. begin
  2952. Result := True;
  2953. if Assigned(OnBusy) then
  2954. begin
  2955. OnBusy(Self, Busy, Result);
  2956. end;
  2957. end;
  2958. function TCustomDirView.StartBusy: Boolean;
  2959. begin
  2960. Result := DoBusy(1);
  2961. end;
  2962. function TCustomDirView.IsBusy: Boolean;
  2963. begin
  2964. Result := DoBusy(0);
  2965. end;
  2966. procedure TCustomDirView.EndBusy;
  2967. begin
  2968. DoBusy(-1);
  2969. end;
  2970. procedure TCustomDirView.BusyOperation(Operation: TBusyOperation);
  2971. begin
  2972. if StartBusy then
  2973. try
  2974. Operation;
  2975. finally
  2976. EndBusy;
  2977. end;
  2978. end;
  2979. initialization
  2980. DropSourceControl := nil;
  2981. SetLength(WinDir, MAX_PATH);
  2982. SetLength(WinDir, GetWindowsDirectory(PChar(WinDir), MAX_PATH));
  2983. SetLength(TempDir, MAX_PATH);
  2984. SetLength(TempDir, GetTempPath(MAX_PATH, PChar(TempDir)));
  2985. SpecialFolderLocation(CSIDL_PERSONAL, UserDocumentDirectory);
  2986. WinDir := IncludeTrailingPathDelimiter(WinDir);
  2987. TempDir := IncludeTrailingPathDelimiter(TempDir);
  2988. finalization
  2989. SetLength(StdDirTypeName, 0);
  2990. SetLength(WinDir, 0);
  2991. SetLength(TempDir, 0);
  2992. end.