umyslv.pas 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UMySLV;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, Types, SysUtils, ComCtrls, ShellCtrls, Controls, Graphics, BGRABitmap,
  7. BGRAVirtualScreen, BGRABitmapTypes, UVolatileScrollBar;
  8. type
  9. PLCShellListViewItemData = ^TLCShellListViewItemData;
  10. TLCShellListViewItemData = record
  11. initialIndex: integer;
  12. caption, filename, typeStr, sizeStr, dateOrDeviceStr: string;
  13. fileSize: int64;
  14. isFolder: boolean;
  15. modification: TDateTime;
  16. image: TBGRABitmap;
  17. imageOwned: boolean;
  18. displayRect: TRect;
  19. isSelected: boolean;
  20. end;
  21. TLCShellListViewData = array of TLCShellListViewItemData;
  22. TFormatTypeEvent = procedure(Sender: TObject; var AType: string) of object;
  23. TSelectItemEvent = procedure(Sender: TObject; Item: Integer; Selected: Boolean) of object;
  24. { TLCShellListView }
  25. TLCShellListView = class
  26. private
  27. FAllowMultiSelect: boolean;
  28. FOnDblClick: TNotifyEvent;
  29. FOnSelectionChanged: TNotifyEvent;
  30. FOnSelectItem: TSelectItemEvent;
  31. FSortColumn: integer;
  32. FVirtualScreen: TBGRAVirtualScreen;
  33. FMask: string;
  34. FObjectTypes: TObjectTypes;
  35. FOnFormatType: TFormatTypeEvent;
  36. FRoot: string;
  37. FData: TLCShellListViewData;
  38. FPreviousResize: TSize;
  39. FFitColumnNeeded: boolean;
  40. FViewStyle: TViewStyle;
  41. FIndexIcon,FIndexName, FIndexSize, FIndexType, FIndexDate: integer;
  42. FUpdateCount: integer;
  43. FColumns: array of record
  44. Name: string;
  45. Width: integer;
  46. Align: TAlignment;
  47. displayRect: TRect;
  48. end;
  49. FActualRowHeight,FIconsPerLine: integer;
  50. FSelectedIndex,FKeySelectionRangeStart: integer;
  51. FVScrollBar: TVolatileScrollBar;
  52. FVerticalScrollPos: integer;
  53. FWantedItemVisible: integer;
  54. FItemsPerPage: integer;
  55. { Setters and getters }
  56. function GetColumnCount: integer;
  57. function GetHeight: integer;
  58. function GetItemCaption(AIndex: integer): string;
  59. function GetItemCount: integer;
  60. function GetItemDevice(AIndex: integer): string;
  61. function GetItemIsFolder(AIndex: integer): boolean;
  62. function GetItemLastModification(AIndex: integer): TDateTime;
  63. function GetItemName(AIndex: integer): string;
  64. function GetItemSelected(AIndex: integer): boolean;
  65. function GetItemType(AIndex: integer): string;
  66. function GetSelectedCount: integer;
  67. function GetViewStyleFit: TViewStyle;
  68. function GetWidth: integer;
  69. procedure SetAllowMultiSelect(AValue: boolean);
  70. procedure SetItemSelected(AIndex: integer; AValue: boolean);
  71. procedure SetMask(const AValue: string);
  72. procedure SetOnDblClick(AValue: TNotifyEvent);
  73. procedure SetOnSelectItem(AValue: TSelectItemEvent);
  74. procedure SetRoot(const AValue: string);
  75. procedure SetSelectedIndex(AValue: integer);
  76. procedure SetSortColumn(AValue: integer);
  77. procedure SetViewStyleFit(AValue: TViewStyle);
  78. procedure SetDisplayRect(AIndex: integer; const ARect:TRect);
  79. protected
  80. FOnSort: TNotifyEvent;
  81. { Methods specific to Lazarus }
  82. procedure PopulateWithRoot();
  83. procedure Redraw(Sender: TObject; ABitmap: TBGRABitmap);
  84. procedure KeyDown(Sender: TObject; var Key: Word;
  85. Shift: TShiftState);
  86. procedure MouseDoubleClick(Sender: TObject);
  87. procedure MouseDown(Sender: TObject; Button: TMouseButton;
  88. Shift: TShiftState; X, Y: Integer);
  89. procedure MouseMove(Sender: TObject; {%H-}Shift: TShiftState; X, Y: Integer);
  90. procedure MouseUp(Sender: TObject; Button: TMouseButton;
  91. {%H-}Shift: TShiftState; X, Y: Integer);
  92. procedure MouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
  93. WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
  94. procedure CompareItem(Sender: TObject; Item1, Item2: PLCShellListViewItemData; {%H-}Data: Integer;
  95. var Compare: Integer);
  96. procedure ColumnClick(Sender: TObject; AColumn: integer);
  97. procedure DoFitColumns(ABitmap: TBGRABitmap; AClientWidth: integer);
  98. function AddColumn(AName: string; AWidth: integer; AAlign:TAlignment): integer;
  99. function GetItemCell(AIndex, AColumn: integer): string; virtual;
  100. procedure Clear;
  101. function GetItemFullName(AIndex: integer): string;
  102. function GetItemDisplayRect(AIndex: integer): TRect;
  103. function InternalSelectAll: boolean;
  104. function InternalDeselectAll(AExcept: integer = -1): boolean;
  105. public
  106. DetailIconSize, SmallIconSize, LargeIconSize, FontHeight, MinimumRowHeight: integer;
  107. SelectAllAction: TObjectTypes;
  108. IconPadding: integer;
  109. BytesCaption: string;
  110. { Basic methods }
  111. procedure Reload;
  112. procedure BeginUpdate;
  113. procedure EndUpdate;
  114. procedure InvalidateView;
  115. procedure Update;
  116. procedure MakeItemVisible(AIndex : integer);
  117. constructor Create(AVirtualScreen: TBGRAVirtualScreen);
  118. procedure VirtualScreenFreed;
  119. destructor Destroy; override;
  120. procedure SetItemImage(AIndex: integer; ABitmap: TBGRABitmap; AOwned: boolean);
  121. function GetItemImage(AIndex: integer): TBGRABitmap;
  122. procedure SetFocus;
  123. function GetItemAt(X,Y: Integer): integer;
  124. procedure DeselectAll;
  125. procedure SelectAll;
  126. procedure Sort;
  127. procedure RemoveItemFromList(AIndex: integer);
  128. function IndexByName(AName: string; ACaseSensitive: boolean): integer;
  129. { Properties }
  130. property Mask: string read FMask write SetMask; // Can be used to conect to other controls
  131. property ObjectTypes: TObjectTypes read FObjectTypes write FObjectTypes;
  132. property Root: string read FRoot write SetRoot;
  133. property ViewStyle: TViewStyle read GetViewStyleFit write SetViewStyleFit;
  134. property OnDblClick: TNotifyEvent read FOnDblClick write SetOnDblClick;
  135. property OnSelectItem: TSelectItemEvent read FOnSelectItem write SetOnSelectItem;
  136. property OnSelectionChanged: TNotifyEvent read FOnSelectionChanged write FOnSelectionChanged;
  137. property SortColumn: integer read FSortColumn write SetSortColumn;
  138. property OnSort: TNotifyEvent read FOnSort write FOnSort;
  139. property OnFormatType: TFormatTypeEvent read FOnFormatType write FOnFormatType;
  140. property ColumnCount: integer read GetColumnCount;
  141. property ItemCount: integer read GetItemCount;
  142. property SelectedIndex: integer read FSelectedIndex write SetSelectedIndex;
  143. property Width: integer read GetWidth;
  144. property Height: integer read GetHeight;
  145. property ItemCaption[AIndex: integer]: string read GetItemCaption;
  146. property ItemFullName[AIndex: integer]: string read GetItemFullName;
  147. property ItemLastModification[AIndex: integer]: TDateTime read GetItemLastModification;
  148. property ItemName[AIndex: integer]: string read GetItemName;
  149. property ItemDisplayRect[AIndex: integer]: TRect read GetItemDisplayRect;
  150. property ItemSelected[AIndex: integer]: boolean read GetItemSelected write SetItemSelected;
  151. property ItemIsFolder[AIndex: integer]: boolean read GetItemIsFolder;
  152. property ItemType[AIndex: integer]: string read GetItemType;
  153. property ItemDevice[AIndex: integer]: string read GetItemDevice;
  154. property AllowMultiSelect: boolean read FAllowMultiSelect write SetAllowMultiSelect;
  155. property SelectedCount: integer read GetSelectedCount;
  156. end;
  157. function FileSizeToStr(ASize: int64; AByteCaption: string): string;
  158. implementation
  159. uses LCLType, UResourceStrings, LazPaintType, LazUTF8, Forms, Math,
  160. UFileSystem, LazFileUtils;
  161. const
  162. ssSnap = {$IFDEF DARWIN}ssMeta{$ELSE}ssCtrl{$ENDIF};
  163. var
  164. SortTarget: TLCShellListView;
  165. function FileSizeToStr(ASize: int64; AByteCaption: string): string;
  166. begin
  167. if ASize < 1024 then
  168. result := IntToStr(ASize) + ' ' + AByteCaption
  169. else if ASize < 1024 * 1024 then
  170. result := FloatToStrF(ASize/1024, ffFixed, 5, 1) + ' kB'
  171. else
  172. result := FloatToStrF(ASize/(1024*1024), ffFixed, 5, 1) + ' MB';
  173. end;
  174. function LCListViewCompare(item1,item2: pointer): integer;
  175. begin
  176. result := 0;
  177. if Assigned(SortTarget) then
  178. SortTarget.CompareItem(SortTarget,item1,item2,0,result);
  179. end;
  180. { TLCShellListView }
  181. procedure TLCShellListView.SetMask(const AValue: string);
  182. begin
  183. if AValue <> FMask then
  184. begin
  185. FMask := AValue;
  186. PopulateWithRoot();
  187. end;
  188. end;
  189. procedure TLCShellListView.SetOnDblClick(AValue: TNotifyEvent);
  190. begin
  191. if FOnDblClick=AValue then Exit;
  192. FOnDblClick:=AValue;
  193. end;
  194. procedure TLCShellListView.SetOnSelectItem(AValue: TSelectItemEvent);
  195. begin
  196. if FOnSelectItem=AValue then Exit;
  197. FOnSelectItem:=AValue;
  198. end;
  199. function TLCShellListView.GetViewStyleFit: TViewStyle;
  200. begin
  201. result := FViewStyle;
  202. end;
  203. function TLCShellListView.GetWidth: integer;
  204. begin
  205. result := FVirtualScreen.Width;
  206. end;
  207. procedure TLCShellListView.SetAllowMultiSelect(AValue: boolean);
  208. var idx: integer;
  209. begin
  210. if FAllowMultiSelect=AValue then Exit;
  211. FAllowMultiSelect:=AValue;
  212. if not AValue then
  213. begin
  214. if SelectedCount > 1 then
  215. begin
  216. idx := SelectedIndex;
  217. DeselectAll;
  218. if idx <> -1 then ItemSelected[idx] := true;
  219. end;
  220. end;
  221. end;
  222. procedure TLCShellListView.SetItemSelected(AIndex: integer; AValue: boolean);
  223. begin
  224. if (AIndex < 0) or (AIndex >= ItemCount) then exit
  225. else
  226. begin
  227. FData[AIndex].isSelected := AValue;
  228. InvalidateView;
  229. end;
  230. end;
  231. function TLCShellListView.GetColumnCount: integer;
  232. begin
  233. result := length(FColumns);
  234. end;
  235. function TLCShellListView.GetHeight: integer;
  236. begin
  237. result := FVirtualScreen.Height;
  238. end;
  239. function TLCShellListView.GetItemCaption(AIndex: integer): string;
  240. begin
  241. if (AIndex < 0) or (AIndex >= ItemCount) then
  242. result := ''
  243. else
  244. result := FData[AIndex].caption;
  245. end;
  246. function TLCShellListView.GetItemCount: integer;
  247. begin
  248. result := length(FData);
  249. end;
  250. function TLCShellListView.GetItemDevice(AIndex: integer): string;
  251. begin
  252. if (AIndex < 0) or (AIndex >= ItemCount) then
  253. result := ''
  254. else
  255. result := FData[AIndex].dateOrDeviceStr;
  256. end;
  257. function TLCShellListView.GetItemIsFolder(AIndex: integer): boolean;
  258. begin
  259. if (AIndex < 0) or (AIndex >= ItemCount) then
  260. result := false
  261. else
  262. result := FData[AIndex].isFolder;
  263. end;
  264. function TLCShellListView.GetItemLastModification(AIndex: integer): TDateTime;
  265. begin
  266. if (AIndex < 0) or (AIndex >= ItemCount) then
  267. result := 0
  268. else
  269. result := FData[AIndex].modification;
  270. end;
  271. function TLCShellListView.GetItemName(AIndex: integer): string;
  272. begin
  273. if (AIndex < 0) or (AIndex >= ItemCount) then
  274. result := ''
  275. else
  276. result := FData[AIndex].filename;
  277. end;
  278. function TLCShellListView.GetItemSelected(AIndex: integer): boolean;
  279. begin
  280. if (AIndex < 0) or (AIndex >= ItemCount) then
  281. result := false
  282. else
  283. result := FData[AIndex].isSelected;
  284. end;
  285. function TLCShellListView.GetItemType(AIndex: integer): string;
  286. begin
  287. if (AIndex < 0) or (AIndex >= ItemCount) then
  288. result := ''
  289. else
  290. result := FData[AIndex].typeStr;
  291. end;
  292. function TLCShellListView.GetSelectedCount: integer;
  293. var
  294. i: Integer;
  295. begin
  296. result := 0;
  297. for i := 0 to ItemCount-1 do
  298. if ItemSelected[i] then inc(result);
  299. end;
  300. procedure TLCShellListView.SetRoot(const AValue: string);
  301. begin
  302. if FRoot <> AValue then
  303. begin
  304. FRoot := AValue;
  305. PopulateWithRoot();
  306. end;
  307. end;
  308. procedure TLCShellListView.SetSelectedIndex(AValue: integer);
  309. begin
  310. if (AValue < 0) or (AValue >= ItemCount) then AValue := -1;
  311. if FSelectedIndex=AValue then Exit;
  312. DeselectAll;
  313. FSelectedIndex:=AValue;
  314. ItemSelected[AValue] := true;
  315. end;
  316. procedure TLCShellListView.SetSortColumn(AValue: integer);
  317. begin
  318. if FSortColumn=AValue then Exit;
  319. FSortColumn:=AValue;
  320. end;
  321. procedure TLCShellListView.SetViewStyleFit(AValue: TViewStyle);
  322. begin
  323. if FViewStyle=AValue then Exit;
  324. FViewStyle := AValue;
  325. FFitColumnNeeded:= true;
  326. FreeAndNil(FVScrollBar);
  327. end;
  328. procedure TLCShellListView.SetDisplayRect(AIndex: integer; const ARect: TRect);
  329. begin
  330. if (AIndex < 0) or (AIndex >= ItemCount) then exit;
  331. FData[AIndex].displayRect := ARect;
  332. end;
  333. procedure TLCShellListView.PopulateWithRoot();
  334. var
  335. i,j: Integer;
  336. Dirs,Files: TFileInfoList;
  337. CurFileName, fileType: string;
  338. CurFileSize: Int64;
  339. dataIndex: integer;
  340. function NewItem: integer;
  341. begin
  342. result := dataIndex;
  343. with FData[dataIndex] do
  344. begin
  345. initialIndex := dataIndex;
  346. caption := '';
  347. filename := '';
  348. typeStr := '';
  349. sizeStr := '';
  350. dateOrDeviceStr := '';
  351. fileSize:= 0;
  352. isFolder := false;
  353. modification := 0;
  354. image := nil;
  355. imageOwned := false;
  356. displayRect := EmptyRect;
  357. isSelected := false;
  358. end;
  359. inc(dataIndex);
  360. end;
  361. var drives: TFileSystemArray;
  362. begin
  363. BeginUpdate;
  364. Clear;
  365. // Check inputs
  366. if Trim(FRoot) = '' then
  367. begin
  368. EndUpdate;
  369. Exit;
  370. end;
  371. FData := nil;
  372. dataIndex := 0;
  373. Files := TFileInfoList.Create;
  374. Dirs := TFileInfoList.Create;
  375. try
  376. if FRoot = ':' then
  377. begin
  378. if FIndexDate <> -1 then FColumns[FIndexDate].Name := rsStorageDevice;
  379. if FObjectTypes * [otFolders] <> [] then
  380. begin
  381. drives := FileManager.GetFileSystems;
  382. setlength(FData, length(drives));
  383. for i := 0 to high(drives) do
  384. with FData[NewItem] do
  385. begin
  386. isFolder := true;
  387. caption := Trim(drives[i].name);
  388. filename := drives[i].path;
  389. if filename <> PathDelim then
  390. filename := ExcludeTrailingPathDelimiter(filename);
  391. if caption = '' then caption := filename;
  392. dateOrDeviceStr := drives[i].device;
  393. typeStr := drives[i].fileSystem;
  394. end;
  395. end;
  396. end else
  397. begin
  398. if FIndexDate <> -1 then FColumns[FIndexDate].Name := rsFileDate;
  399. if FObjectTypes * [otFolders] <> [] then FileManager.GetDirectoryElements(FRoot, '', FObjectTypes * [otFolders], Dirs, fstAlphabet);
  400. if FObjectTypes - [otFolders] <> [] then FileManager.GetDirectoryElements(FRoot, FMask, FObjectTypes - [otFolders], Files, fstAlphabet);
  401. setlength(FData, Dirs.Count+Files.Count);
  402. if Assigned(FOnFormatType) then
  403. begin
  404. fileType := 'Folder';
  405. FOnFormatType(self, fileType);
  406. end else
  407. fileType := rsFolder;
  408. for i := 0 to Dirs.Count - 1 do
  409. if (Dirs.Items[i].Filename <> '') and (Dirs.Items[i].Filename[1] <> '.') then
  410. begin
  411. CurFileName := Dirs.Items[i].Filename;
  412. with FData[NewItem] do
  413. begin
  414. isFolder := true;
  415. filename := CurFileName;
  416. caption := CurFileName;
  417. typeStr := fileType;
  418. end;
  419. end;
  420. for i := 0 to Files.Count - 1 do
  421. begin
  422. j := NewItem;
  423. CurFileName := Files.Items[i].Filename;
  424. CurFileSize := Files.Items[i].Size; // in bytes
  425. FData[j].isFolder := false;
  426. FData[j].filename := CurFileName;
  427. FData[j].caption := ChangeFileExt(CurFileName,'');
  428. FData[j].modification := Files.Items[i].LastModification;
  429. FData[j].fileSize:= CurFileSize;
  430. // Second column - Size
  431. // The raw size in bytes is stored in the data part of the item
  432. FData[j].sizeStr := FileSizeToStr(CurFileSize, BytesCaption);
  433. // Third column - Type
  434. fileType := ExtractFileExt(CurFileName);
  435. if Assigned(FOnFormatType) then FOnFormatType(self, fileType);
  436. FData[j].typeStr := fileType;
  437. FData[j].dateOrDeviceStr := DateToStr(FData[j].modification);
  438. end;
  439. end;
  440. finally
  441. Files.Free;
  442. Dirs.Free;
  443. setlength(FData, dataIndex);
  444. FFitColumnNeeded:= true;
  445. DeselectAll;
  446. EndUpdate;
  447. if SortColumn <> -1 then Sort;
  448. end;
  449. end;
  450. procedure TLCShellListView.Redraw(Sender: TObject; ABitmap: TBGRABitmap);
  451. var
  452. clientArea: TRect;
  453. textHeight,w,h: integer;
  454. maxScrollDetail, maxScrollIcons: integer;
  455. btnColor,btnTxtColor: TBGRAPixel;
  456. txtColor,selTxtColor,selBackColor: TBGRAPixel;
  457. actualIconSize,iconSizeWithPadding: integer;
  458. scrollBarVisible: boolean;
  459. totalIconVSize: integer;
  460. procedure DrawDetails;
  461. var col,x,y,row: integer;
  462. colPos: array of integer;
  463. curY : integer;
  464. maxScroll: integer;
  465. c: TBGRAPixel;
  466. r: TRect;
  467. txt:string;
  468. begin
  469. ABitmap.GradientFill(0,0,w,textHeight, ApplyIntensityFast(btnColor,38000),ApplyIntensityFast(btnColor,26000),
  470. gtLinear, PointF(0,0),PointF(0,textHeight), dmSet);
  471. if (FWantedItemVisible <> -1) and (FWantedItemVisible < ItemCount) then
  472. begin
  473. curY := -(FVerticalScrollPos*FActualRowHeight div 32)+(FWantedItemVisible*FActualRowHeight);
  474. if curY < 0 then FVerticalScrollPos:= FWantedItemVisible*32 else
  475. if textHeight+curY+FActualRowHeight > h then
  476. begin
  477. FVerticalScrollPos:= (FWantedItemVisible+1)*32 - (h-textHeight)*32 div FActualRowHeight;
  478. if FVerticalScrollPos < 0 then FVerticalScrollPos := 0;
  479. end;
  480. FWantedItemVisible:= -1;
  481. FreeAndNil(FVScrollBar);
  482. end;
  483. if scrollBarVisible and not Assigned(FVScrollBar) then
  484. begin
  485. maxScroll := ItemCount*32 - (h-textHeight)*32 div FActualRowHeight + 8;
  486. if maxScroll < 0 then maxScroll := 0;
  487. if FVerticalScrollPos > maxScroll then FVerticalScrollPos:= maxScroll;
  488. FVScrollBar := TVolatileScrollBar.Create(w-VolatileScrollBarSize,textHeight,
  489. VolatileScrollBarSize,h-textHeight,sbVertical,FVerticalScrollPos,0,maxScroll);
  490. end;
  491. if Assigned(FVScrollBar) then
  492. begin
  493. FVScrollBar.Draw(ABitmap);
  494. clientArea := rect(0,textHeight,w-VolatileScrollBarSize,h);
  495. end else
  496. begin
  497. clientArea := rect(0,textHeight,w,h);
  498. FVerticalScrollPos:= 0;
  499. end;
  500. FItemsPerPage:= Size(clientArea).cy div FActualRowHeight;
  501. setlength(colPos,ColumnCount+1);
  502. colPos[0] := 0;
  503. for col := 0 to ColumnCount-1 do
  504. begin
  505. colPos[col+1] := colPos[col] + FColumns[col].Width;
  506. r := rect(colPos[col],0,colPos[col+1],textHeight);
  507. FColumns[col].displayRect := r;
  508. txt := FColumns[col].Name;
  509. if col = SortColumn then
  510. if col = FIndexDate then txt += '▲'
  511. else txt += '▼';
  512. ABitmap.ClipRect := r;
  513. if ABitmap.TextSize(txt).cx > Size(r).cx then
  514. ABitmap.TextOut(r.left,r.top, txt, btnTxtColor, taLeftJustify)
  515. else
  516. case FColumns[col].Align of
  517. taCenter: ABitmap.TextOut((r.left+r.right) div 2,r.top, txt, btnTxtColor, FColumns[col].Align);
  518. taRightJustify: ABitmap.TextOut(r.right,r.top, txt, btnTxtColor, FColumns[col].Align);
  519. else ABitmap.TextOut(r.left,r.top, txt, btnTxtColor, FColumns[col].Align);
  520. end;
  521. ABitmap.NoClip;
  522. end;
  523. curY := textHeight-(FVerticalScrollPos mod 32)*FActualRowHeight div 32;
  524. for row := 0 to ItemCount-1 do SetDisplayRect(row,EmptyRect);
  525. row := FVerticalScrollPos div 32;
  526. while (curY < clientArea.Bottom) and (row < ItemCount) do
  527. begin
  528. r := rect(clientArea.Left,curY,clientArea.Right,curY+FActualRowHeight);
  529. if IntersectRect(r,r,clientArea) then
  530. begin
  531. SetDisplayRect(row,r);
  532. ABitmap.ClipRect := r;
  533. if ItemSelected[row] then
  534. begin
  535. ABitmap.FillRect(r, selBackColor, dmSet);
  536. c := selTxtColor;
  537. end else
  538. c := txtColor;
  539. if GetItemImage(row) <> nil then
  540. begin
  541. x := colPos[FIndexIcon]+(FColumns[FIndexIcon].Width-DetailIconSize) div 2;
  542. y := curY+(FActualRowHeight-DetailIconSize) div 2;
  543. ABitmap.StretchPutImage(RectWithSize(x,y,DetailIconSize,DetailIconSize), GetItemImage(row), dmDrawWithTransparency);
  544. end;
  545. for col := 0 to ColumnCount-1 do
  546. ABitmap.TextRect(rect(colPos[col],curY,colPos[col+1],curY+FActualRowHeight), ' '+GetItemCell(row,col)+' ', FColumns[col].Align, tlCenter, c);
  547. ABitmap.NoClip;
  548. end;
  549. inc(curY, FActualRowHeight);
  550. inc(row);
  551. end;
  552. end;
  553. procedure DrawIcons;
  554. var x,y,item,nx: integer;
  555. r: TRect;
  556. c: TBGRAPixel;
  557. maxScroll: integer;
  558. begin
  559. if (FWantedItemVisible <> -1) and (FWantedItemVisible < ItemCount) then
  560. begin
  561. y := -(FVerticalScrollPos*totalIconVSize div (32*FIconsPerLine))+(FWantedItemVisible div FIconsPerLine)*totalIconVSize;
  562. if y < 0 then FVerticalScrollPos:= (FWantedItemVisible div FIconsPerLine)*32*FIconsPerLine else
  563. if y+totalIconVSize > h then
  564. begin
  565. FVerticalScrollPos:= ((FWantedItemVisible div FIconsPerLine)+1)*32*FIconsPerLine - (h*32*FIconsPerLine div totalIconVSize);
  566. if FVerticalScrollPos < 0 then FVerticalScrollPos := 0;
  567. end;
  568. FWantedItemVisible:= -1;
  569. FreeAndNil(FVScrollBar);
  570. end;
  571. if scrollBarVisible and not Assigned(FVScrollBar) then
  572. begin
  573. maxScroll := ((ItemCount+FIconsPerLine-1) div FIconsPerLine)*32*FIconsPerLine - (h*32*FIconsPerLine div totalIconVSize) + 8*FIconsPerLine;
  574. if maxScroll < 0 then maxScroll := 0;
  575. if FVerticalScrollPos > maxScroll then FVerticalScrollPos:= maxScroll;
  576. FVScrollBar := TVolatileScrollBar.Create(w-VolatileScrollBarSize,0,
  577. VolatileScrollBarSize,h,sbVertical,FVerticalScrollPos,0,maxScroll);
  578. end;
  579. if Assigned(FVScrollBar) then
  580. begin
  581. FVScrollBar.Draw(ABitmap);
  582. clientArea := rect(0,0,w-VolatileScrollBarSize,h);
  583. end else
  584. begin
  585. clientArea := rect(0,0,w,h);
  586. FVerticalScrollPos := 0;
  587. end;
  588. FItemsPerPage:= (Size(clientArea).cy div totalIconVSize)*FIconsPerLine;
  589. for item := 0 to ItemCount-1 do SetDisplayRect(item,EmptyRect);
  590. item := (FVerticalScrollPos div (32*FIconsPerLine))*FIconsPerLine;
  591. x := clientArea.left;
  592. y := clientArea.top - FVerticalScrollPos*totalIconVSize div (32*FIconsPerLine) + (item div FIconsPerLine)*totalIconVSize;
  593. nx := 0;
  594. while item < ItemCount do
  595. begin
  596. r := RectWithSize(x,y,iconSizeWithPadding,totalIconVSize);
  597. if IntersectRect(r,r,clientArea) then
  598. begin
  599. ABitmap.ClipRect := r;
  600. SetDisplayRect(item,r);
  601. if ItemSelected[item] then
  602. begin
  603. ABitmap.FillRect(r, selBackColor, dmSet);
  604. c := selTxtColor;
  605. end else
  606. c := txtColor;
  607. if GetItemImage(item) <> nil then
  608. begin
  609. r := RectWithSize(x+IconPadding,y+IconPadding,actualIconSize,actualIconSize);
  610. ABitmap.StretchPutImage(r,GetItemImage(item),dmDrawWithTransparency);
  611. end;
  612. with ABitmap.TextSize(ItemCaption[item]) do
  613. begin
  614. if cx > iconSizeWithPadding then
  615. ABitmap.TextOut(x,y+IconPadding+actualIconSize,ItemCaption[item],c,taLeftJustify)
  616. else
  617. ABitmap.TextOut(x+(iconSizeWithPadding div 2),y+IconPadding+actualIconSize,ItemCaption[item],c,taCenter)
  618. end;
  619. ABitmap.NoClip;
  620. end;
  621. inc(x,iconSizeWithPadding);
  622. inc(nx);
  623. if nx >= FIconsPerLine then
  624. begin
  625. nx := 0;
  626. x := clientArea.Left;
  627. inc(y,totalIconVSize);
  628. if y >= clientArea.Bottom then break;
  629. end;
  630. inc(item);
  631. end;
  632. end;
  633. var i: integer;
  634. begin
  635. if SelectedIndex = -1 then FKeySelectionRangeStart := -1
  636. else if FKeySelectionRangeStart = -1 then FKeySelectionRangeStart:= SelectedIndex;
  637. for i := 0 to ColumnCount-1 do
  638. FColumns[i].displayRect := EmptyRect;
  639. w := ABitmap.Width;
  640. h := ABitmap.Height;
  641. FItemsPerPage:= 0;
  642. ABitmap.FontHeight := FontHeight;
  643. ABitmap.FontQuality := fqSystemClearType;
  644. FActualRowHeight:= MinimumRowHeight;
  645. textHeight := ABitmap.FontFullHeight+2;
  646. if textHeight > FActualRowHeight then FActualRowHeight:= textHeight;
  647. if (w <> FPreviousResize.cx) or (h <> FPreviousResize.cy) then
  648. begin
  649. FPreviousResize.cx := w;
  650. FPreviousResize.cy := h;
  651. FFitColumnNeeded:= true;
  652. FreeAndNil(FVScrollBar);
  653. end;
  654. if ViewStyle = vsReport then actualIconSize:= DetailIconSize
  655. else if ViewStyle = vsSmallIcon then actualIconSize := SmallIconSize
  656. else if ViewStyle = vsIcon then actualIconSize:= LargeIconSize
  657. else actualIconSize := FActualRowHeight-2;
  658. if actualIconSize+2 > FActualRowHeight then FActualRowHeight:= actualIconSize+2;
  659. iconSizeWithPadding := actualIconSize+IconPadding*2;
  660. FIconsPerLine:= (w-VolatileScrollBarSize) div iconSizeWithPadding;
  661. if FIconsPerLine < 1 then FIconsPerLine:= 1;
  662. totalIconVSize := iconSizeWithPadding+textHeight;
  663. maxScrollDetail := Max(0,ItemCount - ((h-textHeight) div FActualRowHeight));
  664. maxScrollIcons := ((ItemCount+FIconsPerLine-1) div FIconsPerLine)*FIconsPerLine
  665. - (h div totalIconVSize)*FIconsPerLine;
  666. scrollBarVisible:= ((ViewStyle = vsReport) and (maxScrollDetail > 0)) or
  667. ((ViewStyle in[vsSmallIcon,vsIcon]) and (maxScrollIcons > 0));
  668. if FFitColumnNeeded then
  669. begin
  670. if scrollBarVisible then
  671. DoFitColumns(ABitmap, w-VolatileScrollBarSize) else
  672. DoFitColumns(ABitmap, w);
  673. FFitColumnNeeded:= false;
  674. end;
  675. btnColor := ColorToBGRA(ColorToRGB(clBtnFace));
  676. btnTxtColor := ColorToBGRA(ColorToRGB(clBtnText));
  677. txtColor := ColorToBGRA(ColorToRGB(clWindowText));
  678. selTxtColor := ColorToBGRA(ColorToRGB(clHighlightText));
  679. selBackColor := ColorToBGRA(ColorToRGB(clHighlight));
  680. ABitmap.Fill(ColorToBGRA(ColorToRGB(clWindow)));
  681. if ViewStyle = vsReport then DrawDetails else
  682. DrawIcons;
  683. end;
  684. procedure TLCShellListView.KeyDown(Sender: TObject; var Key: Word;
  685. Shift: TShiftState);
  686. procedure KeySelectRange(curItem: integer);
  687. var i: integer;
  688. begin
  689. DeselectAll;
  690. if (ssShift in Shift) and (FKeySelectionRangeStart >= 0) and (FKeySelectionRangeStart < ItemCount) and
  691. FAllowMultiSelect then
  692. begin
  693. i := curItem;
  694. FSelectedIndex:= curItem;
  695. ItemSelected[i] := true;
  696. while i <> FKeySelectionRangeStart do
  697. begin
  698. if i< FKeySelectionRangeStart then inc(i) else dec(i);
  699. ItemSelected[i]:= true;
  700. end;
  701. end else
  702. begin
  703. FSelectedIndex:= curItem;
  704. ItemSelected[FSelectedIndex] := true;
  705. end;
  706. InvalidateView;
  707. if Assigned(FOnSelectItem) then FOnSelectItem(self,curItem,true);
  708. MakeItemVisible(curItem);
  709. end;
  710. begin
  711. if ItemCount = 0 then exit;
  712. if Key in [VK_DOWN,VK_RIGHT,VK_LEFT,VK_UP,VK_PRIOR,VK_NEXT] then
  713. begin
  714. if SelectedIndex = -1 then
  715. begin
  716. Key := 0;
  717. DeselectAll;
  718. FSelectedIndex:= 0;
  719. ItemSelected[0] := true;
  720. InvalidateView;
  721. MakeItemVisible(0);
  722. if Assigned(FOnSelectItem) then FOnSelectItem(self,0,true);
  723. exit;
  724. end
  725. end;
  726. if Key = VK_HOME then
  727. begin
  728. Key := 0;
  729. KeySelectRange(0);
  730. end else
  731. if Key = VK_END then
  732. begin
  733. Key := 0;
  734. KeySelectRange(ItemCount-1);
  735. end else
  736. if ((Key = VK_DOWN) and (ViewStyle in [vsReport,vsList])) or
  737. ((Key = VK_RIGHT) and (ViewStyle in [vsIcon,vsSmallIcon])) then
  738. begin
  739. Key := 0;
  740. if SelectedIndex < ItemCount-1 then KeySelectRange(SelectedIndex+1);
  741. end else
  742. if ((Key = VK_UP) and (ViewStyle = vsReport)) or
  743. ((Key = VK_LEFT) and (ViewStyle = vsIcon)) then
  744. begin
  745. Key := 0;
  746. if SelectedIndex > 0 then KeySelectRange(SelectedIndex-1);
  747. end else
  748. if (Key = VK_DOWN) and (ViewStyle in [vsIcon,vsSmallIcon]) then
  749. begin
  750. Key := 0;
  751. if SelectedIndex < ItemCount-1 then KeySelectRange(Min(ItemCount-1,SelectedIndex+FIconsPerLine));
  752. end else
  753. if (Key = VK_UP) and (ViewStyle in [vsIcon,vsSmallIcon]) then
  754. begin
  755. Key := 0;
  756. if SelectedIndex > 0 then KeySelectRange(Max(0,SelectedIndex-FIconsPerLine));
  757. end else
  758. if (Key = VK_NEXT) and (FItemsPerPage <> 0) then
  759. begin
  760. Key := 0;
  761. if SelectedIndex < ItemCount-1 then KeySelectRange(Min(ItemCount-1,SelectedIndex+FItemsPerPage));
  762. end else
  763. if (Key = VK_PRIOR) and (FItemsPerPage <> 0) then
  764. begin
  765. Key := 0;
  766. if SelectedIndex > 0 then KeySelectRange(Max(0,SelectedIndex-FItemsPerPage));
  767. end else
  768. if (Key = VK_A) and (ssSnap in Shift) then
  769. begin
  770. Key := 0;
  771. SelectAll;
  772. end;
  773. end;
  774. procedure TLCShellListView.MouseDoubleClick(Sender: TObject);
  775. begin
  776. if (SelectedIndex <> -1) and Assigned(FOnDblClick) then
  777. FOnDblClick(self);
  778. end;
  779. procedure TLCShellListView.MouseDown(Sender: TObject; Button: TMouseButton;
  780. Shift: TShiftState; X, Y: Integer);
  781. var i,idx, prevIdx:integer;
  782. keepSelection, selChanged:boolean;
  783. begin
  784. SetFocus;
  785. for i := 0 to ColumnCount-1 do
  786. if PtInRect(Point(x,y),FColumns[i].displayRect) then
  787. begin
  788. ColumnClick(self,i);
  789. exit;
  790. end;
  791. if Assigned(FVScrollBar) and (Button = mbLeft) then
  792. if FVScrollBar.MouseDown(X,Y) then
  793. begin
  794. FVerticalScrollPos:= FVScrollBar.Position;
  795. InvalidateView;
  796. exit;
  797. end;
  798. idx := GetItemAt(X,Y);
  799. keepSelection := (ssSnap in Shift) and FAllowMultiSelect;
  800. selChanged := false;
  801. if not keepSelection and InternalDeselectAll(FSelectedIndex) then selChanged := true;
  802. if (ssShift in Shift) and (FSelectedIndex <> -1) and (idx <> -1) and FAllowMultiSelect then
  803. begin
  804. if idx <> FSelectedIndex then
  805. begin
  806. while idx <> FSelectedIndex do
  807. begin
  808. if FSelectedIndex > idx then dec(FSelectedIndex) else inc(FSelectedIndex);
  809. ItemSelected[FSelectedIndex] := not ItemSelected[FSelectedIndex];
  810. selChanged := true;
  811. end;
  812. if not ItemSelected[FSelectedIndex] then FSelectedIndex := -1;
  813. if Assigned(FOnSelectItem) then FOnSelectItem(self, FSelectedIndex, ItemSelected[FSelectedIndex]);
  814. end;
  815. end else
  816. begin
  817. if keepSelection then
  818. begin
  819. if idx <> -1 then
  820. begin
  821. ItemSelected[idx] := not ItemSelected[idx];
  822. if ItemSelected[idx] then FSelectedIndex := idx
  823. else if idx = FSelectedIndex then FSelectedIndex := -1;
  824. if Assigned(FOnSelectItem) then FOnSelectItem(self, idx, ItemSelected[idx]);
  825. selChanged := true;
  826. end;
  827. end else
  828. if idx <> FSelectedIndex then
  829. begin
  830. if FSelectedIndex <> -1 then
  831. begin
  832. prevIdx := FSelectedIndex;
  833. ItemSelected[prevIdx] := false;
  834. FSelectedIndex := -1;
  835. if Assigned(FOnSelectItem) then FOnSelectItem(self, prevIdx, false);
  836. selChanged := true;
  837. end;
  838. if idx <> -1 then
  839. begin
  840. ItemSelected[idx] := true;
  841. FSelectedIndex := idx;
  842. if Assigned(FOnSelectItem) then FOnSelectItem(self, idx, true);
  843. selChanged := true;
  844. end;
  845. end;
  846. end;
  847. if selChanged then
  848. begin
  849. InvalidateView;
  850. if (FSelectedIndex <> -1) and ItemSelected[FSelectedIndex] then
  851. MakeItemVisible(FSelectedIndex);
  852. if Assigned(FOnSelectionChanged) then FOnSelectionChanged(self);
  853. end;
  854. end;
  855. procedure TLCShellListView.MouseMove(Sender: TObject; Shift: TShiftState; X,
  856. Y: Integer);
  857. begin
  858. if Assigned(FVScrollBar) then
  859. if FVScrollBar.MouseMove(X,Y) then
  860. begin
  861. FVerticalScrollPos:= FVScrollBar.Position;
  862. InvalidateView;
  863. end;
  864. end;
  865. procedure TLCShellListView.MouseUp(Sender: TObject; Button: TMouseButton;
  866. Shift: TShiftState; X, Y: Integer);
  867. begin
  868. if Assigned(FVScrollBar) and (Button = mbLeft) then
  869. if FVScrollBar.MouseUp(X,Y) then
  870. begin
  871. FVerticalScrollPos:= FVScrollBar.Position;
  872. InvalidateView;
  873. end;
  874. end;
  875. procedure TLCShellListView.MouseWheel(Sender: TObject; Shift: TShiftState;
  876. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  877. var Delta: integer;
  878. begin
  879. if Assigned(FVScrollBar) then
  880. begin
  881. Delta := WheelDelta*32;
  882. if ViewStyle = vsIcon then
  883. Delta *= FIconsPerLine
  884. else
  885. Delta *= 2;
  886. FVerticalScrollPos -= Delta div 120;
  887. if FVerticalScrollPos > FVScrollBar.Maximum then FVerticalScrollPos:= FVScrollBar.Maximum;
  888. if FVerticalScrollPos < FVScrollBar.Minimum then FVerticalScrollPos:= FVScrollBar.Minimum;
  889. FreeAndNil(FVScrollBar);
  890. InvalidateView;
  891. Handled := true;
  892. end;
  893. end;
  894. procedure TLCShellListView.CompareItem(Sender: TObject; Item1,
  895. Item2: PLCShellListViewItemData; Data: Integer; var Compare: Integer);
  896. var diff: int64;
  897. diffDate: TDateTime;
  898. procedure CompareIndex;
  899. begin
  900. if item1^.initialIndex > item2^.initialIndex then
  901. compare := 1
  902. else
  903. if item1^.initialIndex < item2^.initialIndex then
  904. compare := -1
  905. else
  906. compare := 0;
  907. end;
  908. procedure CompareName;
  909. begin
  910. if not (Item1^.isFolder xor Item2^.isFolder) then
  911. begin
  912. compare := CompareText(Item1^.filename,Item2^.filename);
  913. if compare = 0 then CompareIndex;
  914. end
  915. else
  916. begin
  917. if Item1^.isFolder then Compare := -1
  918. else Compare := 1;
  919. end;
  920. end;
  921. begin
  922. if SortColumn = FIndexName then CompareName else
  923. if SortColumn = FIndexSize then
  924. begin
  925. diff := Item1^.fileSize-Item2^.fileSize;
  926. if diff < 0 then compare := -1 else
  927. if diff > 0 then compare := 1 else
  928. CompareName;
  929. end else
  930. if SortColumn = FIndexType then
  931. begin
  932. if not (Item1^.isFolder xor Item2^.isFolder) then
  933. begin
  934. compare := UTF8CompareText(Item1^.typeStr,Item2^.typeStr);
  935. if compare = 0 then CompareName;
  936. end else
  937. CompareName;
  938. end else
  939. if SortColumn = FIndexDate then
  940. begin
  941. diffDate:= Item1^.modification-Item2^.modification;
  942. //sort dates descending
  943. if diffDate > 0 then compare := -1 else
  944. if diffDate < 0 then compare := 1 else
  945. CompareName;
  946. end else
  947. CompareIndex;
  948. end;
  949. procedure TLCShellListView.ColumnClick(Sender: TObject; AColumn: integer);
  950. begin
  951. if SortColumn = AColumn then SortColumn := -1
  952. else SortColumn := AColumn;
  953. Sort;
  954. If Assigned(FOnSort) then FOnSort(Sender);
  955. end;
  956. procedure TLCShellListView.Reload;
  957. begin
  958. PopulateWithRoot();
  959. end;
  960. procedure TLCShellListView.BeginUpdate;
  961. begin
  962. inc(FUpdateCount);
  963. end;
  964. procedure TLCShellListView.EndUpdate;
  965. begin
  966. if FUpdateCount > 0 then
  967. begin
  968. dec(FUpdateCount);
  969. if FUpdateCount = 0 then InvalidateView;
  970. end;
  971. if FSelectedIndex >= ItemCount then FSelectedIndex:= -1;
  972. FreeAndNil(FVScrollBar);
  973. end;
  974. procedure TLCShellListView.InvalidateView;
  975. begin
  976. if Assigned(FVirtualScreen) then FVirtualScreen.DiscardBitmap;
  977. end;
  978. procedure TLCShellListView.Update;
  979. begin
  980. FVirtualScreen.Update;
  981. end;
  982. procedure TLCShellListView.MakeItemVisible(AIndex: integer);
  983. begin
  984. FWantedItemVisible := AIndex;
  985. InvalidateView;
  986. end;
  987. procedure TLCShellListView.DoFitColumns(ABitmap: TBGRABitmap; AClientWidth: integer);
  988. var i,j,curSize,totalSize: integer;
  989. colSizes: array of integer;
  990. sizeA: integer;
  991. s: string;
  992. begin
  993. if (ItemCount = 0) or (ColumnCount = 0) then exit;
  994. setlength(colSizes,ColumnCount);
  995. sizeA := ABitmap.TextSize('a').cx;
  996. for j := 0 to ColumnCount-1 do
  997. colSizes[j] := sizeA;
  998. colSizes[FIndexName] := sizeA*8;
  999. colSizes[FIndexIcon] := DetailIconSize+2;
  1000. for j := 0 to ColumnCount-1 do
  1001. begin
  1002. s := FColumns[j].Name;
  1003. if s <> '' then
  1004. begin
  1005. curSize := ABitmap.TextSize(' '+s+' ').cx;
  1006. if curSize > colSizes[j] then colSizes[j] := curSize;
  1007. end;
  1008. end;
  1009. for i := 0 to ItemCount-1 do
  1010. begin
  1011. for j := 0 to ColumnCount-1 do
  1012. if j <> FIndexName then
  1013. begin
  1014. s := GetItemCell(i,j);
  1015. if s <> '' then
  1016. begin
  1017. curSize := ABitmap.TextSize(' '+s+' ').cx;
  1018. if curSize > colSizes[j] then colSizes[j] := curSize;
  1019. end;
  1020. end;
  1021. end;
  1022. BeginUpdate;
  1023. for j := 0 to ColumnCount-1 do
  1024. FColumns[j].Width := colSizes[j];
  1025. totalSize := 0;
  1026. for j := 0 to ColumnCount-1 do
  1027. inc(totalSize, colSizes[j]);
  1028. if totalSize < AClientWidth then inc(colSizes[FIndexName], AClientWidth-totalSize);
  1029. FColumns[FIndexName].Width := colSizes[FIndexName];
  1030. EndUpdate;
  1031. end;
  1032. function TLCShellListView.AddColumn(AName: string; AWidth: integer;
  1033. AAlign: TAlignment): integer;
  1034. begin
  1035. setlength(FColumns, length(FColumns)+1);
  1036. with FColumns[high(FColumns)] do
  1037. begin
  1038. Name := AName;
  1039. Width := AWidth;
  1040. Align:= AAlign;
  1041. end;
  1042. result := high(FColumns);
  1043. end;
  1044. function TLCShellListView.GetItemCell(AIndex, AColumn: integer): string;
  1045. begin
  1046. result := '';
  1047. if (AIndex < 0) or (AIndex >= ItemCount) then exit;
  1048. if AColumn = FIndexName then result := FData[AIndex].caption;
  1049. if AColumn = FIndexSize then result := FData[AIndex].sizeStr;
  1050. if AColumn = FIndexType then result := FData[AIndex].typeStr;
  1051. if AColumn = FIndexDate then result := FData[AIndex].dateOrDeviceStr;
  1052. end;
  1053. procedure TLCShellListView.Clear;
  1054. var i: integer;
  1055. begin
  1056. for I := 0 to ItemCount-1 do
  1057. SetItemImage(I,nil,false);
  1058. FData := nil;
  1059. FSelectedIndex:= -1;
  1060. if FUpdateCount = 0 then InvalidateView;
  1061. end;
  1062. constructor TLCShellListView.Create(AVirtualScreen: TBGRAVirtualScreen);
  1063. begin
  1064. BytesCaption:= rsBytes;
  1065. FVirtualScreen := AVirtualScreen;
  1066. FVScrollBar := nil;
  1067. FAllowMultiSelect:= true;
  1068. FVirtualScreen.OnRedraw := @Redraw;
  1069. FVirtualScreen.OnKeyDown := @KeyDown;
  1070. FVirtualScreen.OnDblClick := @MouseDoubleClick;
  1071. FVirtualScreen.OnMouseDown := @MouseDown;
  1072. FVirtualScreen.OnMouseMove := @MouseMove;
  1073. FVirtualScreen.OnMouseUp := @MouseUp;
  1074. FVirtualScreen.OnMouseWheel:= @MouseWheel;
  1075. FIndexIcon := AddColumn('',50,taCenter);
  1076. FIndexName := AddColumn(rsFileName,200,taLeftJustify);
  1077. FIndexSize := AddColumn(rsFileSize,80,taCenter);
  1078. FIndexType := AddColumn(rsFileType,80,taCenter);
  1079. FIndexDate := AddColumn(rsFileDate,80,taCenter);
  1080. FViewStyle:= vsReport;
  1081. FFitColumnNeeded:= true;
  1082. FontHeight := ScaleY(13,OriginalDPI);
  1083. FSelectedIndex:= -1;
  1084. FVerticalScrollPos := 0;
  1085. SmallIconSize := round(ScaleX(64,OriginalDPI)/32)*32;
  1086. LargeIconSize:= SmallIconSize*2;
  1087. DetailIconSize:= SmallIconSize;
  1088. IconPadding := 8;
  1089. FObjectTypes := [otFolders, otNonFolders];
  1090. FSortColumn:= -1;
  1091. SelectAllAction := [otFolders, otNonFolders];
  1092. end;
  1093. procedure TLCShellListView.VirtualScreenFreed;
  1094. begin
  1095. FVirtualScreen := nil;
  1096. end;
  1097. destructor TLCShellListView.Destroy;
  1098. begin
  1099. Clear;
  1100. if Assigned(FVirtualScreen) then
  1101. begin
  1102. FVirtualScreen.OnRedraw := nil;
  1103. FVirtualScreen.OnKeyDown := nil;
  1104. FVirtualScreen.OnDblClick := nil;
  1105. FVirtualScreen.OnMouseDown := nil;
  1106. FVirtualScreen.OnMouseMove := nil;
  1107. FVirtualScreen.OnMouseUp := nil;
  1108. FVirtualScreen.OnMouseWheel := nil;
  1109. end;
  1110. FreeAndNil(FVScrollBar);
  1111. inherited Destroy;
  1112. end;
  1113. procedure TLCShellListView.SetItemImage(AIndex: integer; ABitmap: TBGRABitmap;
  1114. AOwned: boolean);
  1115. begin
  1116. if (AIndex < 0) or (AIndex >= ItemCount) then exit;
  1117. with FData[AIndex] do
  1118. begin
  1119. if imageOwned then FreeAndNil(image);
  1120. image := ABitmap;
  1121. imageOwned := AOwned and (ABitmap <> nil);
  1122. InvalidateView;
  1123. end;
  1124. end;
  1125. function TLCShellListView.GetItemImage(AIndex: integer): TBGRABitmap;
  1126. begin
  1127. if (AIndex < 0) or (AIndex >= ItemCount) then
  1128. result := nil
  1129. else
  1130. Result := FData[AIndex].image;
  1131. end;
  1132. function TLCShellListView.GetItemFullName(AIndex: integer): string;
  1133. begin
  1134. if (AIndex < 0) or (AIndex >= ItemCount) then
  1135. result := ''
  1136. else
  1137. if FRoot = ':' then
  1138. result := FData[AIndex].filename
  1139. else
  1140. Result := IncludeTrailingPathDelimiter(FRoot) + FData[AIndex].filename;
  1141. end;
  1142. procedure TLCShellListView.SetFocus;
  1143. begin
  1144. SafeSetFocus(FVirtualScreen);
  1145. end;
  1146. function TLCShellListView.GetItemDisplayRect(AIndex: integer): TRect;
  1147. begin
  1148. if (AIndex < 0) or (AIndex >= ItemCount) then
  1149. result := EmptyRect
  1150. else
  1151. result := FData[AIndex].displayRect;
  1152. end;
  1153. function TLCShellListView.InternalSelectAll: boolean;
  1154. var i:integer;
  1155. begin
  1156. result:= false;
  1157. for i := 0 to ItemCount-1 do
  1158. if not FData[i].isSelected and
  1159. ((FData[i].isFolder and (otFolders in SelectAllAction)) or
  1160. (not FData[i].isFolder and (otNonFolders in SelectAllAction))) then
  1161. begin
  1162. FData[i].isSelected := true;
  1163. result := true;
  1164. end;
  1165. for i := 0 to ItemCount-1 do
  1166. if FData[i].isSelected and
  1167. ((FData[i].isFolder and not (otFolders in SelectAllAction)) or
  1168. (not FData[i].isFolder and not (otNonFolders in SelectAllAction))) then
  1169. begin
  1170. FData[i].isSelected := false;
  1171. result := true;
  1172. end;
  1173. if result then InvalidateView;
  1174. end;
  1175. function TLCShellListView.InternalDeselectAll(AExcept: integer): boolean;
  1176. var i:integer;
  1177. begin
  1178. result:= false;
  1179. for i := 0 to ItemCount-1 do
  1180. if (i <> AExcept) and FData[i].isSelected then
  1181. begin
  1182. FData[i].isSelected := false;
  1183. if FSelectedIndex = i then
  1184. begin
  1185. FSelectedIndex := -1;
  1186. if Assigned(FOnSelectItem) then FOnSelectItem(self, i, False);
  1187. end;
  1188. result := true;
  1189. end;
  1190. if result then InvalidateView;
  1191. end;
  1192. procedure TLCShellListView.Sort;
  1193. var lst: TList;
  1194. i: integer;
  1195. sortedData: TLCShellListViewData;
  1196. begin
  1197. lst := TList.Create;
  1198. for i:= 0 to ItemCount-1 do
  1199. lst.Add(@FData[i]);
  1200. SortTarget := self;
  1201. lst.Sort(@LCListViewCompare);
  1202. setlength(sortedData,ItemCount);
  1203. for i := 0 to lst.Count-1 do
  1204. sortedData[i] := PLCShellListViewItemData(lst[i])^;
  1205. FData := sortedData;
  1206. lst.Free;
  1207. if Assigned(FOnSort) then FOnSort(self);
  1208. InvalidateView;
  1209. end;
  1210. procedure TLCShellListView.RemoveItemFromList(AIndex: integer);
  1211. var i: integer;
  1212. begin
  1213. if (AIndex < 0) or (AIndex >= ItemCount) then exit;
  1214. SetItemImage(AIndex,nil,false);
  1215. for i := AIndex to ItemCount-2 do
  1216. FData[i] := FData[i+1];
  1217. setlength(FData, ItemCount-1);
  1218. InvalidateView;
  1219. end;
  1220. function TLCShellListView.IndexByName(AName: string; ACaseSensitive: boolean
  1221. ): integer;
  1222. var
  1223. i: Integer;
  1224. begin
  1225. for i := 0 to ItemCount-1 do
  1226. begin
  1227. if ACaseSensitive and (UTF8CompareStr(AName, ItemName[i])=0) then
  1228. begin
  1229. result := i;
  1230. exit;
  1231. end else
  1232. if not ACaseSensitive and (UTF8CompareText(AName, ItemName[i])=0) then
  1233. begin
  1234. result := i;
  1235. exit;
  1236. end;
  1237. end;
  1238. result := -1;
  1239. end;
  1240. function TLCShellListView.GetItemAt(X, Y: Integer): integer;
  1241. var i: integer;
  1242. p : TPoint;
  1243. begin
  1244. p := Point(X,Y);
  1245. for i := 0 to ItemCount-1 do
  1246. if PtInRect(FData[i].displayRect,p) then
  1247. begin
  1248. result := i;
  1249. exit;
  1250. end;
  1251. result := -1;
  1252. end;
  1253. procedure TLCShellListView.DeselectAll;
  1254. begin
  1255. if InternalDeselectAll then
  1256. if Assigned(FOnSelectionChanged) then FOnSelectionChanged(self);
  1257. end;
  1258. procedure TLCShellListView.SelectAll;
  1259. begin
  1260. if InternalSelectAll then
  1261. if Assigned(FOnSelectionChanged) then FOnSelectionChanged(self);
  1262. end;
  1263. end.