NortonLikeListView.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. unit NortonLikeListView;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  5. ComCtrls, ListViewColProperties, CommCtrl;
  6. type
  7. TCustomNortonLikeListView = class;
  8. TSelectMode = (smAll, smNone, smInvert);
  9. TSelectByMaskEvent = procedure(Control: TCustomNortonLikeListView; Select: Boolean) of object;
  10. TCustomNortonLikeListView = class(TCustomListView)
  11. private
  12. { Private declarations }
  13. FColProperties: TCustomListViewColProperties;
  14. FDontSelectItem: Boolean;
  15. FDontUnSelectItem: Boolean;
  16. FSelCount: Integer;
  17. FNortonLike: Boolean;
  18. FOnSelectByMask: TSelectByMaskEvent;
  19. FLastDeletedItem: TListItem; // aby sme nepocitali smazany item 2x
  20. procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  21. procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;
  22. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  23. procedure WMChar(var Message: TWMChar); message WM_CHAR;
  24. procedure WMNotify(var Message: TWMNotify); message WM_NOTIFY;
  25. procedure CNNotify(var Message: TWMNotify); message CN_NOTIFY;
  26. function GetMarkedCount: Integer;
  27. function GetMarkedFile: TListItem;
  28. protected
  29. { Protected declarations }
  30. FClearingItems: Boolean;
  31. FUpdatingSelection: Integer;
  32. procedure CreateWnd; override;
  33. procedure BeginSelectionUpdate; virtual;
  34. procedure EndSelectionUpdate; virtual;
  35. function CanChangeSelection(Item: TListItem; Select: Boolean): Boolean; virtual;
  36. procedure ClearItems; virtual;
  37. procedure ColRightClick(Column: TListColumn; Point: TPoint); override;
  38. procedure Delete(Item: TListItem); override;
  39. function DoSelectByMask(Select: Boolean): Boolean; virtual;
  40. function ExCanChange(Item: TListItem; Change: Integer;
  41. NewState, OldState: Word): Boolean; dynamic;
  42. procedure InsertItem(Item: TListItem); override;
  43. function NewColProperties: TCustomListViewColProperties; virtual;
  44. procedure FocusSomething; virtual;
  45. function GetItemFromHItem(const Item: TLVItem): TListItem;
  46. function GetValid: Boolean; virtual;
  47. function GetSelCount: Integer; override;
  48. public
  49. { Public declarations }
  50. constructor Create(AOwner: TComponent); override;
  51. function ClosestUnselected(Item: TListItem): TListItem;
  52. procedure SelectAll(Mode: TSelectMode); reintroduce;
  53. procedure SelectCurrentItem(FocusNext: Boolean);
  54. property ColProperties: TCustomListViewColProperties read FColProperties write FColProperties stored False;
  55. //CLEAN property SelCount: Integer read GetSelCount;
  56. property MultiSelect default True;
  57. property NortonLike: Boolean read FNortonLike write FNortonLike default True;
  58. property OnSelectByMask: TSelectByMaskEvent read FOnSelectByMask write FOnSelectByMask;
  59. property MarkedCount: Integer read GetMarkedCount;
  60. property MarkedFile: TListItem read GetMarkedFile;
  61. property Valid: Boolean read GetValid;
  62. end;
  63. type
  64. TNortonLikeListView = class(TCustomNortonLikeListView)
  65. published
  66. { Published declarations }
  67. property Align;
  68. property AllocBy;
  69. property Anchors;
  70. property BiDiMode;
  71. property BorderStyle;
  72. property BorderWidth;
  73. property Checkboxes;
  74. property Color;
  75. //property Columns;
  76. property ColumnClick;
  77. property Constraints;
  78. property Ctl3D;
  79. property Enabled;
  80. property Font;
  81. property FlatScrollBars;
  82. property FullDrag;
  83. property GridLines;
  84. property HideSelection;
  85. property HotTrack;
  86. property HotTrackStyles;
  87. property IconOptions;
  88. property Items;
  89. property LargeImages;
  90. property ReadOnly;
  91. property RowSelect;
  92. property ParentBiDiMode;
  93. property ParentColor;
  94. property ParentFont;
  95. property ParentShowHint;
  96. property PopupMenu;
  97. property ShowColumnHeaders;
  98. property ShowHint;
  99. property SmallImages;
  100. property StateImages;
  101. property TabOrder;
  102. property TabStop;
  103. property ViewStyle;
  104. property Visible;
  105. property OnChange;
  106. property OnChanging;
  107. property OnClick;
  108. property OnColumnClick;
  109. property OnCustomDraw;
  110. property OwnerDraw;
  111. property OnCustomDrawItem;
  112. property OnCustomDrawSubItem;
  113. property OwnerData;
  114. property OnGetImageIndex;
  115. property OnCompare;
  116. property OnData;
  117. property OnDataFind;
  118. property OnDataHint;
  119. property OnDataStateChange;
  120. property OnDblClick;
  121. property OnDeletion;
  122. property OnDrawItem;
  123. property OnEdited;
  124. property OnEditing;
  125. property OnEndDock;
  126. property OnEnter;
  127. property OnExit;
  128. property OnInsert;
  129. property OnKeyDown;
  130. property OnKeyPress;
  131. property OnKeyUp;
  132. property OnMouseDown;
  133. property OnMouseMove;
  134. property OnMouseUp;
  135. property OnResize;
  136. property OnStartDock;
  137. property OnSelectItem;
  138. property NortonLike;
  139. property OnSelectByMask;
  140. property ColProperties;
  141. end;
  142. procedure Register;
  143. implementation
  144. procedure Register;
  145. begin
  146. RegisterComponents('Martin', [TNortonLikeListView]);
  147. end;
  148. { TCustomNortonLikeListView }
  149. constructor TCustomNortonLikeListView.Create(AOwner: TComponent);
  150. begin
  151. inherited Create(AOwner);
  152. FSelCount := 0;
  153. FClearingItems := False;
  154. MultiSelect := True;
  155. FDontSelectItem := False;
  156. FDontUnSelectItem := False;
  157. FNortonLike := True;
  158. FColProperties := NewColProperties;
  159. FLastDeletedItem := nil;
  160. FUpdatingSelection := 0;
  161. end;
  162. procedure TCustomNortonLikeListView.Delete(Item: TListItem);
  163. begin
  164. if (FLastDeletedItem <> Item) and Item.Selected then
  165. Dec(FSelCount);
  166. FLastDeletedItem := Item;
  167. inherited;
  168. FLastDeletedItem := nil;
  169. end;
  170. function TCustomNortonLikeListView.DoSelectByMask(Select: Boolean): Boolean;
  171. begin
  172. if Assigned(FOnSelectByMask) then
  173. begin
  174. FOnSelectByMask(Self, Select);
  175. Result := True;
  176. end
  177. else Result := False;
  178. end;
  179. function TCustomNortonLikeListView.ExCanChange(Item: TListItem; Change: Integer;
  180. NewState, OldState: Word): Boolean;
  181. begin
  182. Assert(Assigned(Item));
  183. Result := True;
  184. if (Change = LVIF_STATE) and
  185. ((((OldState and LVIS_SELECTED) < (NewState and LVIS_SELECTED)) and
  186. ((FDontSelectItem and FNortonLike) or (not CanChangeSelection(Item, True)))) or
  187. (((OldState and LVIS_SELECTED) > (NewState and LVIS_SELECTED)) and
  188. ((FDontUnSelectItem and FNortonLike) or (not CanChangeSelection(Item, False))))) then
  189. begin
  190. if (OldState or LVIS_SELECTED) <> (NewState or LVIS_SELECTED) then
  191. ListView_SetItemState(Handle, Item.Index, NewState,
  192. (NewState or OldState) - LVIS_SELECTED);
  193. Result := False;
  194. end;
  195. end;
  196. function TCustomNortonLikeListView.CanChangeSelection(Item: TListItem;
  197. Select: Boolean): Boolean;
  198. begin
  199. Result := True;
  200. end;
  201. procedure TCustomNortonLikeListView.ClearItems;
  202. begin
  203. Items.BeginUpdate;
  204. try
  205. FClearingItems := True;
  206. Items.Clear;
  207. finally
  208. FSelCount := 0;
  209. FClearingItems := False;
  210. Items.EndUpdate;
  211. end;
  212. end; { ClearItems }
  213. procedure TCustomNortonLikeListView.ColRightClick(Column: TListColumn; Point: TPoint);
  214. var
  215. HitInfo: TLVHitTestInfo;
  216. begin
  217. // Fix: Otherwise we get wrong column when view is horizontally scrolled
  218. HitInfo.pt := Point;
  219. if ListView_SubItemHitTest(Handle, @HitInfo) = 0 then
  220. Column := Columns[HitInfo.iSubItem];
  221. inherited ColRightClick(Column, Point);
  222. end;
  223. function TCustomNortonLikeListView.ClosestUnselected(Item: TListItem): TListItem;
  224. var
  225. Index: Integer;
  226. begin
  227. if Assigned(Item) and (Item.Selected or (NortonLike and (SelCount = 0))) then
  228. begin
  229. Index := Item.Index + 1;
  230. while (Index < Items.Count) and Items[Index].Selected do Inc(Index);
  231. if (Index >= Items.Count) or Items[Index].Selected then
  232. begin
  233. Index := Item.Index - 1;
  234. while (Index >= 0) and Items[Index].Selected do Dec(Index);
  235. end;
  236. if (Index >= 0) and (Index < Items.Count) and (not Items[Index].Selected) then
  237. Result := Items[Index]
  238. else
  239. Result := nil;
  240. end
  241. else Result := Item;
  242. end;
  243. procedure TCustomNortonLikeListView.WMNotify(var Message: TWMNotify);
  244. begin
  245. // disallow resizing of "invisible" (width=0) columns
  246. with PHDNotify(Message.NMHdr)^ do
  247. case Hdr.code of
  248. HDN_BEGINTRACK, HDN_TRACK, HDN_BEGINTRACKW, HDN_TRACKW:
  249. if not ColProperties.Visible[Item] then
  250. begin
  251. Message.Result := 1;
  252. Exit;
  253. end;
  254. end;
  255. inherited;
  256. end;
  257. procedure TCustomNortonLikeListView.CNNotify(var Message: TWMNotify);
  258. begin
  259. with Message do
  260. case NMHdr^.code of
  261. LVN_ITEMCHANGING:
  262. with PNMListView(NMHdr)^ do
  263. if Valid and (not FClearingItems) and (Items[iItem] <> FLastDeletedItem) and
  264. ((not CanChange(Items[iItem], uChanged)) or
  265. (not ExCanChange(Items[iItem], uChanged, uNewState, uOldState)))
  266. then Result := 1;
  267. LVN_ITEMCHANGED:
  268. begin
  269. with PNMListView(NMHdr)^ do
  270. if Valid and (not FClearingItems) and
  271. (uChanged = LVIF_STATE) and (Items[iItem] <> FLastDeletedItem) and
  272. ((uOldState and LVIS_SELECTED) <> (uNewState and LVIS_SELECTED)) then
  273. begin
  274. if (uOldState and LVIS_SELECTED) <> 0 then Dec(FSelCount)
  275. else Inc(FSelCount);
  276. end;
  277. inherited;
  278. end;
  279. else
  280. inherited;
  281. end;
  282. end;
  283. procedure TCustomNortonLikeListView.SelectCurrentItem(FocusNext: Boolean);
  284. var
  285. Item: TListItem;
  286. begin
  287. Item := ItemFocused;
  288. if Item = nil then Item := Items[0];
  289. Item.Selected := not Item.Selected;
  290. if FocusNext then
  291. begin
  292. SendMessage(Handle, WM_KEYDOWN, VK_DOWN, LongInt(0));
  293. end;
  294. end;
  295. procedure TCustomNortonLikeListView.WMKeyDown(var Message: TWMKeyDown);
  296. var
  297. PDontUnSelectItem: Boolean;
  298. PDontSelectItem: Boolean;
  299. begin
  300. if NortonLike and (Message.CharCode = VK_INSERT) then
  301. begin
  302. if Items.Count > 0 then
  303. begin
  304. SelectCurrentItem(True);
  305. Message.Result := 1;
  306. end;
  307. end
  308. else
  309. {if NortonLike and (Message.CharCode = VK_MULTIPLY) then
  310. begin
  311. SelectAll(smInvert)
  312. Message.Result := 1;
  313. end
  314. else }
  315. if (Message.CharCode = VK_ADD) or (Message.CharCode = VK_SUBTRACT) then
  316. begin
  317. if DoSelectByMask((Message.CharCode = VK_ADD)) then
  318. Message.Result := 1;
  319. end
  320. else
  321. if (Message.CharCode in [VK_SPACE, VK_PRIOR, VK_NEXT, VK_END, VK_HOME, VK_LEFT,
  322. VK_UP, VK_RIGHT, VK_DOWN, VK_SELECT]) then
  323. begin
  324. PDontSelectItem := FDontUnSelectItem;
  325. PDontUnSelectItem := FDontUnSelectItem;
  326. FDontSelectItem := True;
  327. FDontUnSelectItem := True;
  328. try
  329. inherited;
  330. finally
  331. FDontSelectItem := PDontSelectItem;
  332. FDontUnSelectItem := PDontUnSelectItem;
  333. end;
  334. end
  335. else inherited;
  336. end;
  337. procedure TCustomNortonLikeListView.WMChar(var Message: TWMChar);
  338. var
  339. PDontUnSelectItem: Boolean;
  340. PDontSelectItem: Boolean;
  341. begin
  342. if NortonLike and (Message.CharCode = Byte(' ')) then
  343. begin
  344. if (GetKeyState(VK_CONTROL) >= 0) then
  345. begin
  346. if Assigned(ItemFocused) then
  347. ItemFocused.Selected := not ItemFocused.Selected;
  348. end
  349. else inherited;
  350. end
  351. else
  352. begin
  353. PDontSelectItem := FDontUnSelectItem;
  354. PDontUnSelectItem := FDontUnSelectItem;
  355. FDontSelectItem := True;
  356. FDontUnSelectItem := True;
  357. try
  358. inherited;
  359. finally
  360. FDontSelectItem := PDontSelectItem;
  361. FDontUnSelectItem := PDontUnSelectItem;
  362. end;
  363. end;
  364. end;
  365. procedure TCustomNortonLikeListView.FocusSomething;
  366. begin
  367. if Valid and (Items.Count > 0) and not Assigned(ItemFocused) then
  368. begin
  369. if NortonLike then SendMessage(Handle, WM_KEYDOWN, VK_DOWN, LongInt(0));
  370. if not Assigned(ItemFocused) then
  371. ItemFocused := Items[0];
  372. end;
  373. if Assigned(ItemFocused) then
  374. ItemFocused.MakeVisible(False);
  375. end;
  376. procedure TCustomNortonLikeListView.SelectAll(Mode: TSelectMode);
  377. var
  378. Index: Integer;
  379. Item: TListItem;
  380. begin
  381. BeginSelectionUpdate;
  382. try
  383. for Index := 0 to Items.Count - 1 do
  384. begin
  385. Item := Items[Index];
  386. case Mode of
  387. smAll: Item.Selected := True;
  388. smNone: Item.Selected := False;
  389. smInvert: Item.Selected := not Item.Selected;
  390. end;
  391. end;
  392. finally
  393. EndSelectionUpdate;
  394. end;
  395. end;
  396. procedure TCustomNortonLikeListView.WMLButtonDown(var Message: TWMLButtonDown);
  397. var
  398. PDontUnSelectItem: Boolean;
  399. PDontSelectItem: Boolean;
  400. Shift: TShiftState;
  401. begin
  402. Shift := KeysToShiftState(Message.Keys);
  403. PDontSelectItem := FDontUnSelectItem;
  404. PDontUnSelectItem := FDontUnSelectItem;
  405. FDontSelectItem := ((Shift * [ssCtrl, ssShift]) = []);
  406. FDontUnSelectItem := ((Shift * [ssCtrl]) = []);
  407. try
  408. inherited;
  409. finally
  410. FDontSelectItem := PDontSelectItem;
  411. FDontUnSelectItem := PDontUnSelectItem;
  412. end;
  413. end;
  414. procedure TCustomNortonLikeListView.WMRButtonDown(var Message: TWMRButtonDown);
  415. var
  416. PDontUnSelectItem: Boolean;
  417. PDontSelectItem: Boolean;
  418. begin
  419. PDontSelectItem := FDontUnSelectItem;
  420. PDontUnSelectItem := FDontUnSelectItem;
  421. FDontSelectItem := True;
  422. FDontUnSelectItem := True;
  423. try
  424. inherited;
  425. finally
  426. FDontSelectItem := PDontSelectItem;
  427. FDontUnSelectItem := PDontUnSelectItem;
  428. end;
  429. end;
  430. function TCustomNortonLikeListView.GetMarkedFile: TListItem;
  431. begin
  432. if Assigned(Selected) then Result := Selected
  433. else
  434. if Assigned(ItemFocused) and NortonLike then Result := ItemFocused
  435. else Result := nil;
  436. end;
  437. function TCustomNortonLikeListView.GetSelCount: Integer;
  438. begin
  439. Result := FSelCount;
  440. end;
  441. procedure TCustomNortonLikeListView.InsertItem(Item: TListItem);
  442. begin
  443. inherited;
  444. if Item.Selected then Inc(FSelCount);
  445. end;
  446. function TCustomNortonLikeListView.NewColProperties: TCustomListViewColProperties;
  447. begin
  448. Result := TListViewColProperties.Create(Self, 5);
  449. end;
  450. function TCustomNortonLikeListView.GetItemFromHItem(const Item: TLVItem): TListItem;
  451. begin
  452. with Item do
  453. if (state and LVIF_PARAM) <> 0 then Result := Pointer(lParam)
  454. else Result := Items[iItem];
  455. end;
  456. function TCustomNortonLikeListView.GetMarkedCount: Integer;
  457. begin
  458. if (SelCount > 0) or (not NortonLike) then Result := SelCount
  459. else
  460. if Assigned(ItemFocused) then Result := 1
  461. else Result := 0;
  462. end;
  463. function TCustomNortonLikeListView.GetValid: Boolean;
  464. begin
  465. // Note that TCustomDirView::GetValid don't inherit
  466. // this method because of optimalization
  467. Result := (not (csDestroying in ComponentState)) and (not FClearingItems);
  468. end;
  469. procedure TCustomNortonLikeListView.BeginSelectionUpdate;
  470. begin
  471. // Higher value is probably some nesting error
  472. Assert(FUpdatingSelection in [0..4]);
  473. Inc(FUpdatingSelection);
  474. end; { BeginUpdatingSelection }
  475. procedure TCustomNortonLikeListView.EndSelectionUpdate;
  476. begin
  477. Assert(FUpdatingSelection > 0);
  478. Dec(FUpdatingSelection);
  479. end; { EndUpdatingSelection }
  480. procedure TCustomNortonLikeListView.CreateWnd;
  481. begin
  482. inherited;
  483. Assert(ColProperties <> nil);
  484. ColProperties.ListViewWndCreated;
  485. end;
  486. end.