NortonLikeListView.pas 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217
  1. unit NortonLikeListView;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  5. ComCtrls, ListViewColProperties, CommCtrl, Menus;
  6. type
  7. TCustomNortonLikeListView = class;
  8. TSelectMode = (smAll, smNone, smInvert);
  9. TNortonLikeMode = (nlOn, nlOff, nlKeyboard);
  10. TSelectMethod = (smNoneYet, smMouse, smKeyboard);
  11. TCustomNortonLikeListView = class(TCustomListView)
  12. private
  13. { Private declarations }
  14. FColProperties: TCustomListViewColProperties;
  15. FDontSelectItem: Boolean;
  16. FDontUnSelectItem: Boolean;
  17. FSelCount: Integer;
  18. FNortonLike: TNortonLikeMode;
  19. FLastDeletedItem: TListItem; // aby sme nepocitali smazany item 2x
  20. FFocusingItem: Boolean;
  21. FManageSelection: Boolean;
  22. FForceUpdateOnItemUnfocus: Boolean;
  23. FFirstSelected: Integer;
  24. FLastSelected: Integer;
  25. FFocused: TDateTime;
  26. FIgnoreSetFocusFrom: THandle;
  27. FSelectingImplicitly: Boolean;
  28. FAnyAndAllSelectedImplicitly: Boolean;
  29. FLButtonDownShiftState: TShiftState;
  30. FLButtonDownPos: TPoint;
  31. FLastSelectMethod: TSelectMethod;
  32. FDarkMode: Boolean;
  33. procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  34. procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;
  35. procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
  36. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  37. procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
  38. procedure WMChar(var Message: TWMChar); message WM_CHAR;
  39. procedure WMNotify(var Msg: TWMNotify); message WM_NOTIFY;
  40. procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
  41. procedure LVMEditLabel(var Message: TMessage); message LVM_EDITLABEL;
  42. procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  43. procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
  44. procedure WMNCDestroy(var Message: TWMNCDestroy); message WM_NCDESTROY;
  45. function GetMarkedCount: Integer;
  46. function GetMarkedFile: TListItem;
  47. procedure ItemSelected(Item: TListItem; Index: Integer);
  48. procedure ItemUnselected(Item: TListItem; Index: Integer);
  49. procedure SelectAll(Mode: TSelectMode; Exclude: TListItem); reintroduce; overload;
  50. procedure WMThemeChanged(var Msg: TMessage); message WM_THEMECHANGED;
  51. procedure SetDarkMode(Value: Boolean);
  52. protected
  53. { Protected declarations }
  54. FClearingItems: Boolean;
  55. FInsertingNewUnselectedItem: Boolean;
  56. FUpdatingSelection: Integer;
  57. FNextCharToIgnore: Word;
  58. FHeaderHandle: HWND;
  59. procedure CreateWnd; override;
  60. procedure DestroyWnd; override;
  61. procedure BeginSelectionUpdate; virtual;
  62. procedure EndSelectionUpdate; virtual;
  63. function CanChangeSelection(Item: TListItem; Select: Boolean): Boolean; virtual;
  64. procedure ClearItems; virtual;
  65. procedure ItemsReordered;
  66. procedure Delete(Item: TListItem); override;
  67. function ExCanChange(Item: TListItem; Change: Integer;
  68. NewState, OldState: Word): Boolean; dynamic;
  69. procedure InsertItem(Item: TListItem); override;
  70. function NewColProperties: TCustomListViewColProperties; virtual; abstract;
  71. procedure FocusSomething(ForceMakeVisible: Boolean); virtual;
  72. function EnableDragOnClick: Boolean; virtual;
  73. function GetItemFromHItem(const Item: TLVItem): TListItem;
  74. function GetValid: Boolean; virtual;
  75. function GetSelCount: Integer; override;
  76. procedure DDBeforeDrag;
  77. function CanEdit(Item: TListItem): Boolean; override;
  78. function GetPopupMenu: TPopupMenu; override;
  79. procedure ChangeScale(M, D: Integer; isDpiChange: Boolean); override;
  80. procedure SetItemSelectedByIndex(Index: Integer; Select: Boolean);
  81. function GetItemSelectedByIndex(Index: Integer): Boolean;
  82. procedure MakeTopItem(Item: TListItem);
  83. procedure UpdateDarkMode;
  84. public
  85. { Public declarations }
  86. constructor Create(AOwner: TComponent); override;
  87. destructor Destroy; override;
  88. function ClosestUnselected(Item: TListItem): TListItem;
  89. procedure SelectAll(Mode: TSelectMode); reintroduce; overload;
  90. procedure SelectCurrentItem(FocusNext: Boolean);
  91. function GetNextItem(StartItem: TListItem; Direction: TSearchDirection;
  92. States: TItemStates): TListItem;
  93. procedure MakeProgressVisible(Item: TListItem);
  94. procedure FocusItem(Item: TListItem);
  95. function IsItemVisible(Item: TListItem): Boolean;
  96. property ColProperties: TCustomListViewColProperties read FColProperties write FColProperties stored False;
  97. property MultiSelect default True;
  98. property NortonLike: TNortonLikeMode read FNortonLike write FNortonLike default nlOn;
  99. property MarkedCount: Integer read GetMarkedCount;
  100. property MarkedFile: TListItem read GetMarkedFile;
  101. property Valid: Boolean read GetValid;
  102. property LastSelectMethod: TSelectMethod read FLastSelectMethod;
  103. property DarkMode: Boolean read FDarkMode write SetDarkMode;
  104. end;
  105. implementation
  106. uses
  107. PasTools, Types, Winapi.UxTheme;
  108. { TCustomNortonLikeListView }
  109. constructor TCustomNortonLikeListView.Create(AOwner: TComponent);
  110. begin
  111. inherited Create(AOwner);
  112. FSelCount := 0;
  113. FFirstSelected := -1;
  114. FLastSelected := -1;
  115. FClearingItems := False;
  116. FInsertingNewUnselectedItem := False;
  117. MultiSelect := True;
  118. FDontSelectItem := False;
  119. FDontUnSelectItem := False;
  120. FNortonLike := nlOn;
  121. FColProperties := NewColProperties;
  122. FLastDeletedItem := nil;
  123. FUpdatingSelection := 0;
  124. FFocusingItem := False;
  125. FLastSelectMethod := smNoneYet;
  126. // Since Windows Vista, native GetNextItem for selection stops working
  127. // once we disallow deselecting any item (see ExCanChange).
  128. // So we need to manage selection state ourselves
  129. // All supported Windows versions have the bug (last time tested on Windows 11 23H2 22631),
  130. // keeping the variable only as a way to tag all related code
  131. FManageSelection := True;
  132. FFocused := 0;
  133. FIgnoreSetFocusFrom := INVALID_HANDLE_VALUE;
  134. // On Windows 7 we have to force item update when it looses focus,
  135. // otherwise some remnants of focus rectangle remain
  136. // Doing the same on WinXP makes list view down from the item flicker,
  137. // so we avoid this there.
  138. // Not sure about Vista
  139. FForceUpdateOnItemUnfocus := IsWin7;
  140. FNextCharToIgnore := 0;
  141. FDarkMode := False;
  142. end;
  143. destructor TCustomNortonLikeListView.Destroy;
  144. begin
  145. FColProperties.Free;
  146. inherited;
  147. end;
  148. procedure TCustomNortonLikeListView.ItemSelected(Item: TListItem; Index: Integer);
  149. begin
  150. Inc(FSelCount);
  151. if FSelectingImplicitly and (FSelCount = 1) then
  152. begin
  153. FAnyAndAllSelectedImplicitly := True;
  154. end
  155. else
  156. if not FSelectingImplicitly then
  157. begin
  158. FAnyAndAllSelectedImplicitly := False;
  159. end;
  160. if FManageSelection then
  161. begin
  162. if Index < 0 then
  163. Index := Item.Index;
  164. if FSelCount = 1 then
  165. begin
  166. Assert(FFirstSelected < 0);
  167. FFirstSelected := Index;
  168. Assert(FLastSelected < 0);
  169. FLastSelected := Index;
  170. end
  171. else
  172. begin
  173. // if reference is not assigned, do not assign it as we
  174. // cannot be sure that the item is actually first/last
  175. if (FFirstSelected >= 0) and (Index < FFirstSelected) then
  176. FFirstSelected := Index;
  177. if (FLastSelected >= 0) and (Index > FLastSelected) then
  178. FLastSelected := Index;
  179. end;
  180. end;
  181. end;
  182. procedure TCustomNortonLikeListView.ItemUnselected(Item: TListItem; Index: Integer);
  183. begin
  184. Dec(FSelCount);
  185. if (FSelCount = 0) or (not FSelectingImplicitly) then
  186. begin
  187. FAnyAndAllSelectedImplicitly := False;
  188. end;
  189. if FManageSelection then
  190. begin
  191. if Index < 0 then
  192. Index := Item.Index;
  193. if FFirstSelected = Index then
  194. begin
  195. if FSelCount = 1 then
  196. FFirstSelected := FLastSelected // may be -1
  197. else
  198. FFirstSelected := -1;
  199. end;
  200. if FLastSelected = Index then
  201. begin
  202. if FSelCount = 1 then
  203. FLastSelected := FFirstSelected // may be -1
  204. else
  205. FLastSelected := -1;
  206. end;
  207. end;
  208. end;
  209. procedure TCustomNortonLikeListView.Delete(Item: TListItem);
  210. var
  211. Index: Integer;
  212. begin
  213. if (FLastDeletedItem <> Item) and (not FClearingItems) then
  214. begin
  215. Index := Item.Index;
  216. if GetItemSelectedByIndex(Index) then
  217. ItemUnselected(Item, Index);
  218. if FManageSelection then
  219. begin
  220. if (FLastSelected >= 0) and (Index <= FLastSelected) then
  221. Dec(FLastSelected);
  222. if (FFirstSelected >= 0) and (Index <= FFirstSelected) then
  223. Dec(FFirstSelected);
  224. end;
  225. end;
  226. FLastDeletedItem := Item;
  227. inherited;
  228. FLastDeletedItem := nil;
  229. end;
  230. function TCustomNortonLikeListView.ExCanChange(Item: TListItem; Change: Integer;
  231. NewState, OldState: Word): Boolean;
  232. begin
  233. Assert(Assigned(Item));
  234. Result := True;
  235. if (Change = LVIF_STATE) and
  236. ((((OldState and LVIS_SELECTED) < (NewState and LVIS_SELECTED)) and
  237. (FDontSelectItem or (not CanChangeSelection(Item, True)))) or
  238. (((OldState and LVIS_SELECTED) > (NewState and LVIS_SELECTED)) and
  239. (FDontUnSelectItem or (not CanChangeSelection(Item, False))))) then
  240. begin
  241. if (OldState or LVIS_SELECTED) <> (NewState or LVIS_SELECTED) then
  242. begin
  243. ListView_SetItemState(Handle, Item.Index, NewState,
  244. (NewState or OldState) - LVIS_SELECTED);
  245. end;
  246. Result := False;
  247. end;
  248. end;
  249. function TCustomNortonLikeListView.CanChangeSelection(Item: TListItem;
  250. Select: Boolean): Boolean;
  251. begin
  252. Result := True;
  253. end;
  254. procedure TCustomNortonLikeListView.ClearItems;
  255. begin
  256. Items.BeginUpdate;
  257. try
  258. FClearingItems := True;
  259. Items.Clear;
  260. finally
  261. FSelCount := 0;
  262. if FManageSelection then
  263. begin
  264. FFirstSelected := -1;
  265. FLastSelected := -1;
  266. end;
  267. FClearingItems := False;
  268. Items.EndUpdate;
  269. end;
  270. end; { ClearItems }
  271. procedure TCustomNortonLikeListView.ItemsReordered;
  272. begin
  273. if FManageSelection then
  274. begin
  275. FFirstSelected := -1;
  276. FLastSelected := -1;
  277. end;
  278. end;
  279. function TCustomNortonLikeListView.ClosestUnselected(Item: TListItem): TListItem;
  280. var
  281. Index: Integer;
  282. begin
  283. if Assigned(Item) and (Item.Selected or ((NortonLike <> nlOff) and (SelCount = 0))) then
  284. begin
  285. Index := Item.Index + 1;
  286. while (Index < Items.Count) and GetItemSelectedByIndex(Index) do Inc(Index);
  287. if (Index >= Items.Count) or GetItemSelectedByIndex(Index) then
  288. begin
  289. Index := Item.Index - 1;
  290. while (Index >= 0) and GetItemSelectedByIndex(Index) do Dec(Index);
  291. end;
  292. if (Index >= 0) and (Index < Items.Count) and (not GetItemSelectedByIndex(Index)) then
  293. Result := Items[Index]
  294. else
  295. Result := nil;
  296. end
  297. else Result := Item;
  298. end;
  299. function TCustomNortonLikeListView.GetPopupMenu: TPopupMenu;
  300. begin
  301. // While editing pretend that we do not have a popup menu.
  302. // Otherwise Ctrl+V is swallowed by the TWinControl.CNKeyDown,
  303. // when it finds out (TWinControl.IsMenuKey) that there's a command with Ctrl+V shortcut in the list view context menu
  304. // (the "paste" file action)
  305. if IsEditing then
  306. begin
  307. Result := nil;
  308. end
  309. else
  310. begin
  311. Result := inherited;
  312. end;
  313. end;
  314. procedure TCustomNortonLikeListView.WMNotify(var Msg: TWMNotify);
  315. var
  316. HDNotify: PHDNotify;
  317. begin
  318. if (FHeaderHandle <> 0) and (Msg.NMHdr^.hWndFrom = FHeaderHandle) then
  319. begin
  320. HDNotify := PHDNotify(Msg.NMHdr);
  321. // Disallow resizing of "invisible" (width=0) columns.
  322. // (We probably get only Unicode versions of the messages here as
  323. // controls are created as Unicode by VCL)
  324. case HDNotify.Hdr.code of
  325. HDN_BEGINTRACKA, HDN_TRACKA, HDN_BEGINTRACKW, HDN_TRACKW:
  326. if not ColProperties.Visible[HDNotify.Item] then
  327. begin
  328. Msg.Result := 1;
  329. Exit;
  330. end;
  331. // We won't get here when user tries to resize the column by mouse,
  332. // as that's prevented above.
  333. // But we get here when other methods are used
  334. // (the only we know about atm is Ctrl-+ shortcut)
  335. // We are getting this notification also when control is being setup,
  336. // with mask including also other fields, not just HDI_WIDTH.
  337. // While it does not seem to hurt to swallow even those messages,
  338. // not sure it's good thing to do, so we swallow width-only messages only.
  339. // That's why there's "= HDI_WIDTH" not "and HDI_WIDTH <> 0".
  340. HDN_ITEMCHANGINGA, HDN_ITEMCHANGINGW:
  341. if (HDNotify.PItem.Mask = HDI_WIDTH) and
  342. (HDNotify.PItem.cxy <> 0) and
  343. (not ColProperties.Visible[HDNotify.Item]) then
  344. begin
  345. Msg.Result := 1;
  346. Exit;
  347. end;
  348. // This all is to make header text white in dark mode.
  349. NM_CUSTOMDRAW:
  350. if DarkMode and SupportsDarkMode and
  351. GetSysDarkTheme then // When system app theme is light, headers are not dark
  352. begin
  353. with PNMLVCustomDraw(Msg.NMHdr)^ do
  354. begin
  355. if nmcd.dwDrawStage = CDDS_PREPAINT then
  356. begin
  357. inherited;
  358. Msg.Result := Msg.Result or CDRF_NOTIFYITEMDRAW;
  359. Exit;
  360. end
  361. else
  362. if nmcd.dwDrawStage = CDDS_ITEMPREPAINT then
  363. begin
  364. SetTextColor(nmcd.hdc, ColorToRGB(Font.Color));
  365. Msg.Result := CDRF_DODEFAULT;
  366. end;
  367. end;
  368. end
  369. end;
  370. end;
  371. inherited;
  372. end;
  373. procedure TCustomNortonLikeListView.WMThemeChanged(var Msg: TMessage);
  374. begin
  375. if SupportsDarkMode then // To reduce impact
  376. begin
  377. UpdateDarkMode;
  378. RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE);
  379. end;
  380. inherited;
  381. end;
  382. procedure TCustomNortonLikeListView.UpdateDarkMode;
  383. begin
  384. if SupportsDarkMode then // To reduce impact
  385. begin
  386. AllowDarkModeForWindow(Self, DarkMode);
  387. // To update scrollbar theme
  388. SendMessage(Handle, WM_THEMECHANGED, 0, 0);
  389. if FHeaderHandle <> 0 then
  390. begin
  391. AllowDarkModeForWindow(FHeaderHandle, DarkMode);
  392. // Needs to be sent explicitly (does not propagate from the above call)
  393. SendMessage(FHeaderHandle, WM_THEMECHANGED, 0, 0);
  394. end;
  395. end;
  396. end;
  397. procedure TCustomNortonLikeListView.SetDarkMode(Value: Boolean);
  398. begin
  399. if DarkMode <> Value then
  400. begin
  401. FDarkMode := Value;
  402. // Call only when switching to dark more and when switching back to the light mode.
  403. // But not for initial light mode - To reduce an impact of calling an undocumented function.
  404. if HandleAllocated then UpdateDarkMode;
  405. end;
  406. end;
  407. procedure TCustomNortonLikeListView.DDBeforeDrag;
  408. begin
  409. FDontSelectItem := False;
  410. FDontUnSelectItem := False;
  411. end;
  412. procedure TCustomNortonLikeListView.CNNotify(var Message: TWMNotify);
  413. var
  414. Item: TListItem;
  415. begin
  416. with Message do
  417. case NMHdr^.code of
  418. LVN_ITEMCHANGING:
  419. with PNMListView(NMHdr)^ do
  420. begin
  421. Item := Items[iItem];
  422. if Valid and (not FClearingItems) and (Item <> FLastDeletedItem) and
  423. ((not CanChange(Item, uChanged)) or
  424. (not ExCanChange(Item, uChanged, uNewState, uOldState)))
  425. then
  426. begin
  427. Result := 1;
  428. end;
  429. end;
  430. LVN_ITEMCHANGED:
  431. begin
  432. with PNMListView(NMHdr)^ do
  433. begin
  434. Item := Items[iItem];
  435. if Valid and (not FClearingItems) and
  436. (uChanged = LVIF_STATE) and (Item <> FLastDeletedItem) then
  437. begin
  438. if FForceUpdateOnItemUnfocus and
  439. (NortonLike <> nlOff) and
  440. ((uOldState and LVIS_FOCUSED) > (uNewState and LVIS_FOCUSED)) then
  441. begin
  442. // force update, otherwise some remnants of focus rectangle remain
  443. Item.Update;
  444. end;
  445. if (uOldState and LVIS_SELECTED) <> (uNewState and LVIS_SELECTED) then
  446. begin
  447. if (uOldState and LVIS_SELECTED) <> 0 then
  448. begin
  449. ItemUnselected(Item, iItem);
  450. end
  451. else
  452. begin
  453. ItemSelected(Item, iItem);
  454. end;
  455. end;
  456. end;
  457. end;
  458. inherited;
  459. end;
  460. LVN_ENDLABELEDIT:
  461. begin
  462. FIgnoreSetFocusFrom := ListView_GetEditControl(Handle);
  463. inherited;
  464. end;
  465. else
  466. begin
  467. inherited;
  468. end;
  469. end;
  470. end;
  471. procedure TCustomNortonLikeListView.SelectCurrentItem(FocusNext: Boolean);
  472. var
  473. Item: TListItem;
  474. begin
  475. Item := ItemFocused;
  476. if Item = nil then Item := Items[0];
  477. Item.Selected := not Item.Selected;
  478. if FocusNext then
  479. begin
  480. SendMessage(Handle, WM_KEYDOWN, VK_DOWN, LongInt(0));
  481. end;
  482. end;
  483. procedure TCustomNortonLikeListView.WMKeyDown(var Message: TWMKeyDown);
  484. var
  485. PLastSelectMethod: TSelectMethod;
  486. PDontUnSelectItem: Boolean;
  487. PDontSelectItem: Boolean;
  488. begin
  489. FNextCharToIgnore := 0;
  490. if (NortonLike <> nlOff) and (Message.CharCode = VK_INSERT) then
  491. begin
  492. if Items.Count > 0 then
  493. begin
  494. PLastSelectMethod := FLastSelectMethod;
  495. FLastSelectMethod := smKeyboard;
  496. try
  497. SelectCurrentItem(True);
  498. finally
  499. FLastSelectMethod := PLastSelectMethod;
  500. end;
  501. Message.Result := 1;
  502. end;
  503. end
  504. else
  505. if Message.CharCode = VK_ADD then
  506. begin
  507. FNextCharToIgnore := Word('+');
  508. inherited;
  509. end
  510. else
  511. if Message.CharCode = VK_SUBTRACT then
  512. begin
  513. FNextCharToIgnore := Word('-');
  514. inherited;
  515. end
  516. else
  517. if Message.CharCode = VK_MULTIPLY then
  518. begin
  519. FNextCharToIgnore := Word('*');
  520. inherited;
  521. end
  522. else
  523. if (NortonLike <> nlOff) and (Message.CharCode in [VK_LEFT, VK_RIGHT]) and
  524. (ViewStyle = vsReport) and
  525. ((GetWindowLong(Handle, GWL_STYLE) and WS_HSCROLL) = 0) then
  526. begin
  527. if Items.Count > 0 then
  528. begin
  529. // do not focus item directly to make later selecting work
  530. if Message.CharCode = VK_LEFT then
  531. SendMessage(Handle, WM_KEYDOWN, VK_HOME, LongInt(0))
  532. else
  533. SendMessage(Handle, WM_KEYDOWN, VK_END, LongInt(0));
  534. end;
  535. Message.Result := 1;
  536. end
  537. else
  538. if (NortonLike <> nlOff) and (Message.CharCode = VK_SPACE) and
  539. ((KeyDataToShiftState(Message.KeyData) * [ssCtrl]) <> []) then
  540. begin
  541. // prevent Ctrl+Space landing in else branch below,
  542. // this can safely get processed by default handler as Ctrl+Space
  543. // toggles only focused item, not affecting others
  544. PLastSelectMethod := FLastSelectMethod;
  545. FLastSelectMethod := smKeyboard;
  546. try
  547. inherited;
  548. finally
  549. FLastSelectMethod := PLastSelectMethod;
  550. end;
  551. end
  552. else
  553. if (Message.CharCode in [VK_SPACE, VK_PRIOR, VK_NEXT, VK_END, VK_HOME, VK_LEFT,
  554. VK_UP, VK_RIGHT, VK_DOWN, VK_SELECT]) then
  555. begin
  556. PLastSelectMethod := FLastSelectMethod;
  557. PDontSelectItem := FDontSelectItem;
  558. PDontUnSelectItem := FDontUnSelectItem;
  559. FLastSelectMethod := smKeyboard;
  560. FDontSelectItem := FDontSelectItem or
  561. ((NortonLike <> nlOff) and
  562. ((KeyDataToShiftState(Message.KeyData) * [ssShift]) = []));
  563. // Note that Space (selecting toggling) is processed by default handler for WM_CHAR,
  564. // otherwise the below condition would prevent unselection
  565. FDontUnSelectItem :=
  566. FDontUnSelectItem or
  567. (NortonLike = nlOn) or
  568. ((NortonLike = nlKeyboard) and (not FAnyAndAllSelectedImplicitly));
  569. try
  570. inherited;
  571. finally
  572. FDontSelectItem := PDontSelectItem;
  573. FDontUnSelectItem := PDontUnSelectItem;
  574. FLastSelectMethod := PLastSelectMethod;
  575. end;
  576. end
  577. else inherited;
  578. end;
  579. procedure TCustomNortonLikeListView.WMSysCommand(var Message: TWMSysCommand);
  580. begin
  581. // Ugly workaround to avoid Windows beeping when Alt+Grey +/- are pressed
  582. // (for (Us)Select File with Same Ext commands)
  583. // The same for Alt+Enter (for Properties)
  584. if (Message.CmdType = SC_KEYMENU) and
  585. ((Message.Key = Word('+')) or (Message.Key = Word('-')) or (Message.Key = VK_RETURN)) then
  586. begin
  587. Message.Result := 1;
  588. end
  589. else inherited;
  590. end;
  591. procedure TCustomNortonLikeListView.WMChar(var Message: TWMChar);
  592. var
  593. PLastSelectMethod: TSelectMethod;
  594. PDontUnSelectItem: Boolean;
  595. PDontSelectItem: Boolean;
  596. begin
  597. if Message.CharCode = FNextCharToIgnore then
  598. begin
  599. // ugly fix to avoid Windows beeping when these keys are processed by
  600. // WMKeyDown instead of here (WMChar)
  601. Message.Result := 1;
  602. end
  603. else
  604. if (NortonLike <> nlOff) and (Message.CharCode = Byte(' ')) then
  605. begin
  606. if (GetKeyState(VK_CONTROL) >= 0) then
  607. begin
  608. // If not handled in TCustomScpExplorerForm::DirViewKeyPress
  609. if not DoKeyPress(Message) then
  610. begin
  611. if Assigned(ItemFocused) then
  612. ItemFocused.Selected := not ItemFocused.Selected;
  613. end;
  614. end
  615. else inherited;
  616. end
  617. else
  618. begin
  619. PLastSelectMethod := FLastSelectMethod;
  620. PDontSelectItem := FDontSelectItem;
  621. PDontUnSelectItem := FDontUnSelectItem;
  622. FDontSelectItem := FDontSelectItem or (NortonLike <> nlOff);
  623. FLastSelectMethod := smKeyboard;
  624. FDontUnSelectItem :=
  625. FDontUnSelectItem or
  626. (NortonLike = nlOn) or
  627. ((NortonLike = nlKeyboard) and (not FAnyAndAllSelectedImplicitly));
  628. try
  629. inherited;
  630. finally
  631. FLastSelectMethod := PLastSelectMethod;
  632. FDontSelectItem := PDontSelectItem;
  633. FDontUnSelectItem := PDontUnSelectItem;
  634. end;
  635. end;
  636. FNextCharToIgnore := 0;
  637. end;
  638. procedure TCustomNortonLikeListView.FocusSomething(ForceMakeVisible: Boolean);
  639. var
  640. MakeVisible: Boolean;
  641. begin
  642. MakeVisible := ForceMakeVisible;
  643. if Valid and (Items.Count > 0) and not Assigned(ItemFocused) then
  644. begin
  645. MakeVisible := True;
  646. if (NortonLike <> nlOff) then
  647. begin
  648. SendMessage(Handle, WM_KEYDOWN, VK_DOWN, LongInt(0));
  649. end;
  650. if not Assigned(ItemFocused) then
  651. begin
  652. ItemFocused := Items[0];
  653. end;
  654. end;
  655. if MakeVisible and Assigned(ItemFocused) then
  656. begin
  657. ItemFocused.MakeVisible(False);
  658. end;
  659. end;
  660. function TCustomNortonLikeListView.EnableDragOnClick: Boolean;
  661. begin
  662. Result := (not FFocusingItem);
  663. end;
  664. procedure TCustomNortonLikeListView.FocusItem(Item: TListItem);
  665. var
  666. P: TPoint;
  667. PLastSelectMethod: TSelectMethod;
  668. PDontUnSelectItem: Boolean;
  669. PDontSelectItem: Boolean;
  670. WParam: UINT_PTR;
  671. LParam: INT_PTR;
  672. begin
  673. // This whole is replacement for mere ItemFocused := Item
  674. // because that does not reset some internal focused pointer,
  675. // causing subsequent Shift-Click selects range from the first item,
  676. // not from focused item.
  677. Item.MakeVisible(False);
  678. Assert(Focused);
  679. if Focused then
  680. begin
  681. P := Item.GetPosition;
  682. PLastSelectMethod := FLastSelectMethod;
  683. PDontSelectItem := FDontSelectItem;
  684. PDontUnSelectItem := FDontUnSelectItem;
  685. FLastSelectMethod := smNoneYet;
  686. FDontSelectItem := True;
  687. FDontUnSelectItem := True;
  688. FFocusingItem := True;
  689. try
  690. // HACK
  691. // WM_LBUTTONDOWN enters loop, waiting for WM_LBUTTONUP,
  692. // so we have to post it in advance to break the loop immediately
  693. // Without MK_CONTROL, if there are more items selected,
  694. // they won't get unselected on subsequent focus change
  695. // (with explorer-style selection).
  696. // And it also makes the click the least obtrusive, affecting the focused
  697. // file only.
  698. WParam := MK_LBUTTON or MK_CONTROL;
  699. LParam := MAKELPARAM(P.X, P.Y);
  700. PostMessage(Handle, WM_LBUTTONUP, WParam, LParam);
  701. SendMessage(Handle, WM_LBUTTONDOWN, WParam, LParam);
  702. finally
  703. FFocusingItem := False;
  704. FLastSelectMethod := PLastSelectMethod;
  705. FDontSelectItem := PDontSelectItem;
  706. FDontUnSelectItem := PDontUnSelectItem;
  707. end;
  708. end;
  709. if ItemFocused <> Item then
  710. ItemFocused := Item;
  711. end;
  712. // TListItem.Selected needs an index, which is expensively looked up.
  713. // If we know it already, avoid that loop up.
  714. procedure TCustomNortonLikeListView.SetItemSelectedByIndex(Index: Integer; Select: Boolean);
  715. var
  716. State: Integer;
  717. begin
  718. if Select then State := LVIS_SELECTED
  719. else State := 0;
  720. ListView_SetItemState(Handle, Index, State, LVIS_SELECTED);
  721. end;
  722. function TCustomNortonLikeListView.GetItemSelectedByIndex(Index: Integer): Boolean;
  723. begin
  724. Result := (ListView_GetItemState(Handle, Index, LVIS_SELECTED) and LVIS_SELECTED) <> 0;
  725. end;
  726. procedure TCustomNortonLikeListView.SelectAll(Mode: TSelectMode; Exclude: TListItem);
  727. var
  728. Index: Integer;
  729. Item: TListItem;
  730. NewState: Boolean;
  731. begin
  732. BeginSelectionUpdate;
  733. try
  734. // Setting/Querying selected state is expensive.
  735. // This optimization is important for call from TCustomNortonLikeListView.WMLButtonUp in nlKeyboard mode.
  736. if (Mode = smNone) and
  737. // If there are too many, plain iteration is more effective then using GetNextItem
  738. // (though that can be optimized too, by passing index in and out instead of an item pointer)
  739. (FSelCount < Items.Count div 4) then
  740. begin
  741. Item := GetNextItem(nil, sdAll, [isSelected]);
  742. while Assigned(Item) do
  743. begin
  744. if Item <> Exclude then
  745. Item.Selected := False;
  746. Item := GetNextItem(Item, sdAll, [isSelected]);
  747. end;
  748. end
  749. else
  750. begin
  751. for Index := 0 to Items.Count - 1 do
  752. begin
  753. Item := Items[Index];
  754. if Item <> Exclude then
  755. begin
  756. case Mode of
  757. smAll: NewState := True;
  758. smNone: NewState := False;
  759. smInvert: NewState := not GetItemSelectedByIndex(Index);
  760. else
  761. begin
  762. Assert(False);
  763. NewState := False;
  764. end;
  765. end;
  766. SetItemSelectedByIndex(Index, NewState);
  767. end;
  768. end;
  769. end;
  770. finally
  771. EndSelectionUpdate;
  772. end;
  773. end;
  774. procedure TCustomNortonLikeListView.SelectAll(Mode: TSelectMode);
  775. begin
  776. SelectAll(Mode, nil);
  777. end;
  778. procedure TCustomNortonLikeListView.WMLButtonDown(var Message: TWMLButtonDown);
  779. var
  780. PLastSelectMethod: TSelectMethod;
  781. PDontUnSelectItem: Boolean;
  782. PDontSelectItem: Boolean;
  783. PSelectingImplicitly: Boolean;
  784. SelectingImplicitly: Boolean;
  785. Shift: TShiftState;
  786. Item: TListItem;
  787. begin
  788. Shift := KeysToShiftState(Message.Keys);
  789. PLastSelectMethod := FLastSelectMethod;
  790. PDontSelectItem := FDontSelectItem;
  791. PDontUnSelectItem := FDontUnSelectItem;
  792. PSelectingImplicitly := FSelectingImplicitly;
  793. FLastSelectMethod := smMouse;
  794. FDontSelectItem := FDontSelectItem or ((NortonLike = nlOn) and ((Shift * [ssCtrl, ssShift]) = []));
  795. FDontUnSelectItem := FDontUnSelectItem or ((NortonLike = nlOn) and ((Shift * [ssCtrl]) = []));
  796. SelectingImplicitly := ((Shift * [ssCtrl, ssShift]) = []);
  797. if SelectingImplicitly and (NortonLike = nlKeyboard) then
  798. begin
  799. // in general, when clicking, we clear selection only after mouse button is released,
  800. // from within WMLButtonUp, so we know we are not starting dragging,
  801. // so we do not want to clear the selection.
  802. // on the other hand, when clicking outside of the selection,
  803. // we want to explicitly clear the selection, no matter what
  804. Item := GetItemAt(Message.XPos, Message.YPos);
  805. if (Item = nil) or (not Item.Selected) then
  806. SelectAll(smNone);
  807. end;
  808. FSelectingImplicitly := FSelectingImplicitly or SelectingImplicitly;
  809. FLButtonDownShiftState := Shift;
  810. FLButtonDownPos := Point(Message.XPos, Message.YPos);
  811. try
  812. inherited;
  813. finally
  814. FLastSelectMethod := PLastSelectMethod;
  815. FDontSelectItem := PDontSelectItem;
  816. FDontUnSelectItem := PDontUnSelectItem;
  817. FSelectingImplicitly := PSelectingImplicitly;
  818. end;
  819. end;
  820. procedure TCustomNortonLikeListView.WMRButtonDown(var Message: TWMRButtonDown);
  821. var
  822. PLastSelectMethod: TSelectMethod;
  823. PDontUnSelectItem: Boolean;
  824. PDontSelectItem: Boolean;
  825. PSelectingImplicitly: Boolean;
  826. SelectingImplicitly: Boolean;
  827. Shift: TShiftState;
  828. begin
  829. Shift := KeysToShiftState(Message.Keys);
  830. PLastSelectMethod := FLastSelectMethod;
  831. PDontSelectItem := FDontSelectItem;
  832. PDontUnSelectItem := FDontUnSelectItem;
  833. PSelectingImplicitly := FSelectingImplicitly;
  834. FLastSelectMethod := smMouse;
  835. FDontSelectItem := FDontSelectItem or (NortonLike = nlOn);
  836. FDontUnSelectItem := FDontUnSelectItem or (NortonLike = nlOn);
  837. SelectingImplicitly := ((Shift * [ssCtrl, ssShift]) = []);
  838. // TODO unselect all when clicking outside of selection
  839. // (is not done automatically when focused item is not selected)
  840. FSelectingImplicitly := FSelectingImplicitly or SelectingImplicitly;
  841. try
  842. inherited;
  843. finally
  844. FLastSelectMethod := PLastSelectMethod;
  845. FDontSelectItem := PDontSelectItem;
  846. FDontUnSelectItem := PDontUnSelectItem;
  847. FSelectingImplicitly := PSelectingImplicitly;
  848. end;
  849. end;
  850. procedure TCustomNortonLikeListView.WMLButtonUp(var Message: TWMLButtonUp);
  851. var
  852. SelectingImplicitly: Boolean;
  853. Shift: TShiftState;
  854. begin
  855. // Workaround
  856. // For some reason Message.Keys is always 0 here,
  857. // so we use shift state from the LButtonDown as a workaround
  858. Shift := KeysToShiftState(Message.Keys);
  859. SelectingImplicitly :=
  860. ((Shift * [ssCtrl, ssShift]) = []) and
  861. ((FLButtonDownShiftState * [ssCtrl, ssShift]) = []);
  862. if SelectingImplicitly and (csClicked in ControlState) and
  863. (Abs(FLButtonDownPos.X - Message.XPos) <= 4) and
  864. (Abs(FLButtonDownPos.Y - Message.YPos) <= 4) then
  865. begin
  866. SelectAll(smNone, ItemFocused);
  867. // Because condition in ItemSelected is not triggered as we first select
  868. // the new item and then unselect the previous.
  869. // This probably means that we can get rid of the code in ItemSelected.
  870. FAnyAndAllSelectedImplicitly := True;
  871. end;
  872. inherited;
  873. end;
  874. function TCustomNortonLikeListView.GetMarkedFile: TListItem;
  875. begin
  876. if Assigned(Selected) then Result := Selected
  877. else
  878. if Assigned(ItemFocused) and (NortonLike <> nlOff) then Result := ItemFocused
  879. else Result := nil;
  880. end;
  881. function TCustomNortonLikeListView.GetNextItem(StartItem: TListItem;
  882. Direction: TSearchDirection; States: TItemStates): TListItem;
  883. var
  884. Start, Index, First, Last: Integer;
  885. begin
  886. if not FManageSelection then
  887. begin
  888. Result := inherited GetNextItem(StartItem, Direction, States);
  889. end
  890. else
  891. begin
  892. Assert(Direction = sdAll);
  893. if States = [isSelected] then
  894. begin
  895. if FSelCount = 0 then
  896. begin
  897. Result := nil
  898. end
  899. else
  900. if (not Assigned(StartItem)) and (FFirstSelected >= 0) then
  901. begin
  902. Result := Items[FFirstSelected]
  903. end
  904. else
  905. begin
  906. if Assigned(StartItem) then
  907. Start := StartItem.Index
  908. else
  909. Start := -1;
  910. if (FFirstSelected >= 0) and (Start < FFirstSelected) then
  911. First := FFirstSelected
  912. else
  913. First := Start + 1;
  914. if FLastSelected >= 0 then
  915. Last := FLastSelected
  916. else
  917. Last := Items.Count - 1;
  918. if Start > Last then
  919. begin
  920. Result := nil;
  921. end
  922. else
  923. begin
  924. Index := First;
  925. while (Index <= Last) and (not GetItemSelectedByIndex(Index)) do
  926. begin
  927. Inc(Index);
  928. end;
  929. if Index > Last then
  930. begin
  931. Result := nil;
  932. if (Start >= 0) and GetItemSelectedByIndex(Start) then
  933. begin
  934. Assert((FLastSelected < 0) or (FLastSelected = Start));
  935. FLastSelected := Start;
  936. end;
  937. end
  938. else
  939. begin
  940. Result := Items[Index];
  941. Assert(GetItemSelectedByIndex(Index));
  942. if not Assigned(StartItem) then
  943. begin
  944. Assert((FFirstSelected < 0) or (FFirstSelected = Index));
  945. FFirstSelected := Index;
  946. end;
  947. end;
  948. end;
  949. end;
  950. end
  951. else
  952. if States = [isCut] then
  953. begin
  954. Result := inherited GetNextItem(StartItem, Direction, States);
  955. end
  956. else
  957. if States = [] then
  958. begin
  959. if Assigned(StartItem) then
  960. Start := StartItem.Index
  961. else
  962. Start := -1;
  963. Inc(Start);
  964. if Start < Items.Count then
  965. Result := Items[Start]
  966. else
  967. Result := nil;
  968. end
  969. else
  970. begin
  971. Assert(False);
  972. Result := nil;
  973. end;
  974. end;
  975. end;
  976. function TCustomNortonLikeListView.GetSelCount: Integer;
  977. begin
  978. Result := FSelCount;
  979. end;
  980. procedure TCustomNortonLikeListView.InsertItem(Item: TListItem);
  981. begin
  982. inherited;
  983. if (not FInsertingNewUnselectedItem) and // Optimization to avoid expensive Item.Selected
  984. Item.Selected then
  985. begin
  986. ItemSelected(Item, -1);
  987. end;
  988. end;
  989. function TCustomNortonLikeListView.GetItemFromHItem(const Item: TLVItem): TListItem;
  990. begin
  991. with Item do
  992. if (state and LVIF_PARAM) <> 0 then Result := Pointer(lParam)
  993. else Result := Items[iItem];
  994. end;
  995. function TCustomNortonLikeListView.GetMarkedCount: Integer;
  996. begin
  997. if (SelCount > 0) or (NortonLike = nlOff) then Result := SelCount
  998. else
  999. if Assigned(ItemFocused) then Result := 1
  1000. else Result := 0;
  1001. end;
  1002. function TCustomNortonLikeListView.GetValid: Boolean;
  1003. begin
  1004. // Note that TCustomDirView::GetValid don't inherit
  1005. // this method because of optimalization
  1006. Result := (not (csDestroying in ComponentState)) and (not FClearingItems);
  1007. end;
  1008. procedure TCustomNortonLikeListView.BeginSelectionUpdate;
  1009. begin
  1010. // Higher value is probably some nesting error
  1011. Assert(FUpdatingSelection in [0..4]);
  1012. Inc(FUpdatingSelection);
  1013. end; { BeginUpdatingSelection }
  1014. procedure TCustomNortonLikeListView.EndSelectionUpdate;
  1015. begin
  1016. Assert(FUpdatingSelection > 0);
  1017. Dec(FUpdatingSelection);
  1018. end; { EndUpdatingSelection }
  1019. procedure TCustomNortonLikeListView.WMNCDestroy(var Message: TWMNCDestroy);
  1020. begin
  1021. // VCLCOPY
  1022. FHeaderHandle := 0;
  1023. inherited;
  1024. end;
  1025. procedure TCustomNortonLikeListView.CreateWnd;
  1026. begin
  1027. try
  1028. Assert(ColProperties <> nil);
  1029. inherited;
  1030. // VCL gets the handle from WM_CREATE
  1031. FHeaderHandle := ListView_GetHeader(Handle);
  1032. ColProperties.ListViewWndCreated;
  1033. if SupportsDarkMode then
  1034. begin
  1035. // This enables dark mode - List view itself supports dark mode somewhat even in the our 'Explorer' theme.
  1036. // The 'ItemsView' has better (Explorer-like) dark mode selection color, but on the other hand it does not have dark scrollbars.
  1037. // win32-darkmode has ugly fix for that (FixDarkScrollBar), which we do not want to employ.
  1038. // The 'DarkMode_Explorer' uses the standard selection color (bright blue).
  1039. // Enables dark headers:
  1040. SetWindowTheme(FHeaderHandle, 'ItemsView', nil);
  1041. if DarkMode then UpdateDarkMode;
  1042. end;
  1043. finally
  1044. end;
  1045. end;
  1046. procedure TCustomNortonLikeListView.DestroyWnd;
  1047. begin
  1048. ColProperties.ListViewWndDestroying;
  1049. try
  1050. inherited;
  1051. finally
  1052. ColProperties.ListViewWndDestroyed;
  1053. end;
  1054. end;
  1055. procedure TCustomNortonLikeListView.LVMEditLabel(var Message: TMessage);
  1056. begin
  1057. // explicitly requesting editing (e.g. F2),
  1058. // so we do not care anymore when the view was focused
  1059. FFocused := 0;
  1060. inherited;
  1061. end;
  1062. function TCustomNortonLikeListView.CanEdit(Item: TListItem): Boolean;
  1063. var
  1064. N: TDateTime;
  1065. Delta: Double;
  1066. begin
  1067. N := Now;
  1068. Result := inherited CanEdit(Item);
  1069. if Result and (FFocused > 0) then
  1070. begin
  1071. Delta := N - FFocused;
  1072. // it takes little more than 500ms to trigger editing after click
  1073. Result := Delta > (750.0/MSecsPerDay);
  1074. end;
  1075. FFocused := 0;
  1076. end;
  1077. procedure TCustomNortonLikeListView.WMSetFocus(var Message: TWMSetFocus);
  1078. begin
  1079. inherited;
  1080. if Message.FocusedWnd <> FIgnoreSetFocusFrom then
  1081. FFocused := Now;
  1082. end;
  1083. procedure TCustomNortonLikeListView.CMWantSpecialKey(var Message: TCMWantSpecialKey);
  1084. begin
  1085. inherited;
  1086. if IsEditing and (Message.CharCode = VK_TAB) then
  1087. Message.Result := 1;
  1088. end;
  1089. procedure TCustomNortonLikeListView.MakeTopItem(Item: TListItem);
  1090. begin
  1091. Scroll(0, Item.Top - TopItem.Top);
  1092. end;
  1093. procedure TCustomNortonLikeListView.MakeProgressVisible(Item: TListItem);
  1094. var
  1095. DisplayRect: TRect;
  1096. begin
  1097. if ViewStyle = vsReport then
  1098. begin
  1099. DisplayRect := Item.DisplayRect(drBounds);
  1100. if DisplayRect.Bottom > ClientHeight then
  1101. begin
  1102. MakeTopItem(Item);
  1103. end;
  1104. end;
  1105. Item.MakeVisible(False);
  1106. end;
  1107. function TCustomNortonLikeListView.IsItemVisible(Item: TListItem): Boolean;
  1108. begin
  1109. Result := (ListView_IsItemVisible(Handle, Item.Index) <> 0);
  1110. end;
  1111. procedure TCustomNortonLikeListView.ChangeScale(M, D: Integer; isDpiChange: Boolean);
  1112. begin
  1113. if M <> D then
  1114. begin
  1115. // When font is scaled, while the control is being re-created, previous font is restored once
  1116. // read from the persistence data in TCustomListView.CreateWnd.
  1117. // Requiring handle, makes sure the re-create phase is closed.
  1118. // We could limit impact by checking ControlHasRecreationPersistenceData,
  1119. // but for now, we actually prefer larger impact to test this change better.
  1120. HandleNeeded;
  1121. end;
  1122. inherited;
  1123. ColProperties.ChangeScale(M, D);
  1124. end;
  1125. end.